remove the DNS option from proto-static, it will be handled by generic code
[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_MAX,
204 };
205
206 static const struct uci_parse_option opts[__OPT_MAX] = {
207 [OPT_IPADDR] = { .name = "ipaddr" },
208 [OPT_IP6ADDR] = { .name = "ip6addr" },
209 [OPT_NETMASK] = { .name = "netmask", .type = UCI_TYPE_STRING },
210 [OPT_GATEWAY] = { .name = "gateway", .type = UCI_TYPE_STRING },
211 [OPT_IP6GW] = { .name = "ip6gw", .type = UCI_TYPE_STRING },
212 };
213
214 struct interface_proto_state *
215 static_attach(struct proto_handler *h, struct interface *iface,
216 struct uci_section *s)
217 {
218 struct uci_option *tb[__OPT_MAX];
219 struct uci_element *e;
220 struct in_addr ina = {};
221 const char *error = NULL;
222 int netmask = 32;
223 int i;
224 struct static_proto_settings ps;
225
226 memset(&ps, 0, sizeof(ps));
227 uci_parse_section(s, opts, __OPT_MAX, tb);
228
229 if (tb[OPT_NETMASK]) {
230 if (!inet_aton(tb[OPT_NETMASK]->v.string, &ina)) {
231 error = "INVALID_NETMASK";
232 goto error;
233 }
234
235 netmask = 32 - fls(~(ntohl(ina.s_addr)));
236 }
237
238 if (tb[OPT_IPADDR]) {
239 if (tb[OPT_IPADDR]->type == UCI_TYPE_STRING) {
240 ps.n_v4 = 1;
241 ps.v4 = alloca(sizeof(struct v4_addr));
242 if (!parse_v4(tb[OPT_IPADDR]->v.string, ps.v4, netmask))
243 goto invalid_addr;
244 } else {
245 i = 0;
246 ps.n_v4 = count_list_entries(tb[OPT_IPADDR]);
247 ps.v4 = alloca(sizeof(struct v4_addr) * ps.n_v4);
248 uci_foreach_element(&tb[OPT_IPADDR]->v.list, e) {
249 if (!parse_v4(e->name, &ps.v4[i++], netmask))
250 goto invalid_addr;
251 }
252 }
253 }
254
255 if (tb[OPT_IP6ADDR]) {
256 if (tb[OPT_IP6ADDR]->type == UCI_TYPE_STRING) {
257 ps.n_v6 = 1;
258 ps.v6 = alloca(sizeof(struct v6_addr));
259 ps.v6->prefix = netmask;
260 if (!parse_v6(tb[OPT_IP6ADDR]->v.string, ps.v6, netmask))
261 goto invalid_addr;
262 } else {
263 i = 0;
264 ps.n_v6 = count_list_entries(tb[OPT_IP6ADDR]);
265 ps.v6 = alloca(sizeof(struct v6_addr) * ps.n_v6);
266 uci_foreach_element(&tb[OPT_IP6ADDR]->v.list, e) {
267 if (!parse_v6(e->name, &ps.v6[i++], netmask))
268 goto invalid_addr;
269 }
270 }
271 }
272
273 if (!ps.n_v4 && !ps.n_v6) {
274 error = "NO_ADDRESS";
275 goto error;
276 }
277
278 if (ps.n_v4 && tb[OPT_GATEWAY]) {
279 if (!inet_pton(AF_INET, tb[OPT_GATEWAY]->v.string, &ps.ipv4gw)) {
280 error = "INVALID_GATEWAY";
281 goto error;
282 }
283 ps.flags |= STATIC_F_IPV4GW;
284 }
285
286 if (ps.n_v6 && tb[OPT_IP6GW]) {
287 if (!inet_pton(AF_INET6, tb[OPT_IP6GW]->v.string, &ps.ipv6gw)) {
288 error = "INVALID_GATEWAY";
289 goto error;
290 }
291 ps.flags |= STATIC_F_IPV6GW;
292 }
293
294 return static_create_state(iface, &ps);
295
296 invalid_addr:
297 error = "INVALID_ADDRESS";
298
299 error:
300 interface_add_error(iface, "proto-static", error, NULL, 0);
301 return NULL;
302 }
303
304 static struct proto_handler static_proto = {
305 .name = "static",
306 .attach = static_attach,
307 };
308
309 static void __init
310 static_proto_init(void)
311 {
312 add_proto_handler(&static_proto);
313 }