55e77d5b70fefef1cc95fb169ce003f59caf63f0
[project/netifd.git] / proto-static.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4
5 #include <arpa/inet.h>
6 #include <netinet/in.h>
7
8 #include "netifd.h"
9 #include "interface.h"
10 #include "proto.h"
11 #include "system.h"
12
13 struct v4_addr {
14         unsigned int prefix;
15         struct in_addr addr;
16 };
17
18 struct v6_addr {
19         unsigned int prefix;
20         struct in6_addr addr;
21 };
22
23 enum static_proto_flags {
24         STATIC_F_IPV4GW         = (1 << 0),
25         STATIC_F_IPV6GW         = (1 << 1),
26 };
27
28 struct static_proto_settings {
29         uint32_t flags;
30
31         int n_v4;
32         struct v4_addr *v4;
33
34         int n_v6;
35         struct v6_addr *v6;
36
37         struct in_addr ipv4gw;
38         struct in6_addr ipv6gw;
39 };
40
41 struct static_proto_state {
42     struct interface_proto_state proto;
43         struct interface *iface;
44
45         struct static_proto_settings s;
46 };
47
48 static int
49 static_handler(struct interface_proto_state *proto,
50                enum interface_proto_cmd cmd, bool force)
51 {
52         struct static_proto_state *state;
53         struct static_proto_settings *ps;
54         struct device *dev;
55         int ret = 0;
56         int i;
57
58         state = container_of(proto, struct static_proto_state, proto);
59         ps = &state->s;
60         dev = state->iface->main_dev.dev;
61
62         switch (cmd) {
63         case PROTO_CMD_SETUP:
64                 for (i = 0; i < state->s.n_v4; i++) {
65                         if (ret)
66                                 break;
67                         ret = system_add_address(dev, AF_INET,
68                                 &ps->v4[i].addr, ps->v4[i].prefix);
69                 }
70                 for (i = 0; i < state->s.n_v6; i++) {
71                         if (ret)
72                                 break;
73                         ret = system_add_address(dev, AF_INET6,
74                                 &ps->v6[i].addr, ps->v6[i].prefix);
75                 }
76
77                 if (!ret)
78                         return 0;
79
80                 interface_add_error(state->iface, "proto-static",
81                         "SET_ADDRESS_FAILED", NULL, 0);
82                 /* fall through */
83
84         case PROTO_CMD_TEARDOWN:
85                 for (i = 0; i < ps->n_v4; i++)
86                         system_del_address(dev, AF_INET, &ps->v4[i].addr);
87                 for (i = 0; i < ps->n_v6; i++)
88                         system_del_address(dev, AF_INET6, &ps->v6[i].addr);
89                 break;
90         }
91         return ret;
92 }
93
94 static void
95 static_free(struct interface_proto_state *proto)
96 {
97         struct static_proto_state *state;
98
99         state = container_of(proto, struct static_proto_state, proto);
100         free(state);
101 }
102
103 struct interface_proto_state *
104 static_create_state(struct interface *iface, struct static_proto_settings *ps)
105 {
106         struct static_proto_state *state;
107         int v4_len = sizeof(struct v4_addr) * ps->n_v4;
108         int v6_len = sizeof(struct v6_addr) * ps->n_v6;
109         void *next;
110
111         state = calloc(1, sizeof(*state) + v4_len + v6_len);
112         state->iface = iface;
113         state->proto.free = static_free;
114         state->proto.handler = static_handler;
115         state->proto.flags = PROTO_FLAG_IMMEDIATE;
116         memcpy(&state->s, ps, sizeof(state->s));
117
118         next = (void *) (state + 1);
119
120         if (ps->n_v4) {
121                 ps->v4 = next;
122                 memcpy(next, ps->v4, sizeof(struct v4_addr) * ps->n_v4);
123
124                 next = ps->v4 + ps->n_v4;
125         }
126
127         if (ps->n_v6) {
128                 ps->v6 = next;
129                 memcpy(next, ps->v6, sizeof(struct v6_addr) * ps->n_v6);
130         }
131
132         return &state->proto;
133 }
134
135 static bool
136 split_netmask(char *str, unsigned int *netmask)
137 {
138         char *delim, *err = NULL;
139
140         delim = strchr(str, '/');
141         if (delim) {
142                 *(delim++) = 0;
143
144                 *netmask = strtoul(delim, &err, 10);
145                 if (err && *err)
146                         return false;
147         }
148         return true;
149 }
150
151 static int
152 parse_ip_and_netmask(int af, const char *str, void *addr, unsigned int *netmask)
153 {
154         char *astr = alloca(strlen(str) + 1);
155
156         strcpy(astr, str);
157         if (!split_netmask(astr, netmask))
158                 return 0;
159
160         if (af == AF_INET6) {
161                 if (*netmask > 128)
162                         return 0;
163         } else {
164                 if (*netmask > 32)
165                         return 0;
166         }
167
168         return inet_pton(af, str, addr);
169 }
170
171 static int
172 parse_v4(const char *str, struct v4_addr *v4, int netmask)
173 {
174         v4->prefix = netmask;
175         return parse_ip_and_netmask(AF_INET, str, &v4->addr, &v4->prefix);
176 }
177
178 static int
179 parse_v6(const char *str, struct v6_addr *v6, int netmask)
180 {
181         v6->prefix = netmask;
182         return parse_ip_and_netmask(AF_INET6, str, &v6->addr, &v6->prefix);
183 }
184
185 static int
186 count_list_entries(struct uci_option *o)
187 {
188         struct uci_element *e;
189         int n = 0;
190
191         uci_foreach_element(&o->v.list, e)
192                 n++;
193
194         return n;
195 }
196
197 enum {
198         OPT_IPADDR,
199         OPT_IP6ADDR,
200         OPT_NETMASK,
201         OPT_GATEWAY,
202         OPT_IP6GW,
203         OPT_DNS,
204         __OPT_MAX,
205 };
206
207 static const struct uci_parse_option opts[__OPT_MAX] = {
208         [OPT_IPADDR] = { .name = "ipaddr" },
209         [OPT_IP6ADDR] = { .name = "ip6addr" },
210         [OPT_NETMASK] = { .name = "netmask", .type = UCI_TYPE_STRING },
211         [OPT_GATEWAY] = { .name = "gateway", .type = UCI_TYPE_STRING },
212         [OPT_IP6GW] = { .name = "ip6gw", .type = UCI_TYPE_STRING },
213         [OPT_DNS] = { .name = "dns" },
214 };
215
216 struct interface_proto_state *
217 static_attach(struct proto_handler *h, struct interface *iface,
218               struct uci_section *s)
219 {
220         struct uci_option *tb[__OPT_MAX];
221         struct uci_element *e;
222         struct in_addr ina = {};
223         const char *error = NULL;
224         int netmask = 32;
225         int i;
226         struct static_proto_settings ps;
227
228         memset(&ps, 0, sizeof(ps));
229         uci_parse_section(s, opts, __OPT_MAX, tb);
230
231         if (tb[OPT_NETMASK]) {
232                 if (!inet_aton(tb[OPT_NETMASK]->v.string, &ina)) {
233                         error = "INVALID_NETMASK";
234                         goto error;
235                 }
236
237                 netmask = 32 - fls(~(ntohl(ina.s_addr)));
238         }
239
240         if (tb[OPT_IPADDR]) {
241                 if (tb[OPT_IPADDR]->type == UCI_TYPE_STRING) {
242                         ps.n_v4 = 1;
243                         ps.v4 = alloca(sizeof(struct v4_addr));
244                         if (!parse_v4(tb[OPT_IPADDR]->v.string, ps.v4, netmask))
245                                 goto invalid_addr;
246                 } else {
247                         i = 0;
248                         ps.n_v4 = count_list_entries(tb[OPT_IPADDR]);
249                         ps.v4 = alloca(sizeof(struct v4_addr) * ps.n_v4);
250                         uci_foreach_element(&tb[OPT_IPADDR]->v.list, e) {
251                                 if (!parse_v4(e->name, &ps.v4[i++], netmask))
252                                         goto invalid_addr;
253                         }
254                 }
255         }
256
257         if (tb[OPT_IP6ADDR]) {
258                 if (tb[OPT_IP6ADDR]->type == UCI_TYPE_STRING) {
259                         ps.n_v6 = 1;
260                         ps.v6 = alloca(sizeof(struct v6_addr));
261                         ps.v6->prefix = netmask;
262                         if (!parse_v6(tb[OPT_IP6ADDR]->v.string, ps.v6, netmask))
263                                 goto invalid_addr;
264                 } else {
265                         i = 0;
266                         ps.n_v6 = count_list_entries(tb[OPT_IP6ADDR]);
267                         ps.v6 = alloca(sizeof(struct v6_addr) * ps.n_v6);
268                         uci_foreach_element(&tb[OPT_IP6ADDR]->v.list, e) {
269                                 if (!parse_v6(e->name, &ps.v6[i++], netmask))
270                                         goto invalid_addr;
271                         }
272                 }
273         }
274
275         if (!ps.n_v4 && !ps.n_v6) {
276                 error = "NO_ADDRESS";
277                 goto error;
278         }
279
280         if (ps.n_v4 && tb[OPT_GATEWAY]) {
281                 if (!inet_pton(AF_INET, tb[OPT_GATEWAY]->v.string, &ps.ipv4gw)) {
282                         error = "INVALID_GATEWAY";
283                         goto error;
284                 }
285                 ps.flags |= STATIC_F_IPV4GW;
286         }
287
288         if (ps.n_v6 && tb[OPT_IP6GW]) {
289                 if (!inet_pton(AF_INET6, tb[OPT_IP6GW]->v.string, &ps.ipv6gw)) {
290                         error = "INVALID_GATEWAY";
291                         goto error;
292                 }
293                 ps.flags |= STATIC_F_IPV6GW;
294         }
295
296         return static_create_state(iface, &ps);
297
298 invalid_addr:
299         error = "INVALID_ADDRESS";
300
301 error:
302         interface_add_error(iface, "proto-static", error, NULL, 0);
303         return NULL;
304 }
305
306 static struct proto_handler static_proto = {
307         .name = "static",
308         .attach = static_attach,
309 };
310
311 static void __init
312 static_proto_init(void)
313 {
314         add_proto_handler(&static_proto);
315 }