move address handling to common 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 static_proto_state {
14         struct interface_proto_state proto;
15
16         struct uci_section *section;
17         struct interface *iface;
18 };
19
20 static bool
21 split_netmask(char *str, unsigned int *netmask)
22 {
23         char *delim, *err = NULL;
24
25         delim = strchr(str, '/');
26         if (delim) {
27                 *(delim++) = 0;
28
29                 *netmask = strtoul(delim, &err, 10);
30                 if (err && *err)
31                         return false;
32         }
33         return true;
34 }
35
36 static int
37 parse_ip_and_netmask(int af, const char *str, void *addr, unsigned int *netmask)
38 {
39         char *astr = alloca(strlen(str) + 1);
40
41         strcpy(astr, str);
42         if (!split_netmask(astr, netmask))
43                 return 0;
44
45         if (af == AF_INET6) {
46                 if (*netmask > 128)
47                         return 0;
48         } else {
49                 if (*netmask > 32)
50                         return 0;
51         }
52
53         return inet_pton(af, str, addr);
54 }
55
56 static bool
57 parse_addr(struct static_proto_state *state, const char *str, bool v6, int mask)
58 {
59         struct interface_addr *addr;
60         int af = v6 ? AF_INET6 : AF_INET;
61
62         addr = calloc(1, sizeof(*addr));
63         addr->flags = v6 ? IFADDR_INET6 : IFADDR_INET4;
64         addr->ctx = state;
65         addr->mask = mask;
66         if (!parse_ip_and_netmask(af, str, &addr->addr, &addr->mask)) {
67                 interface_add_error(state->iface, "proto-static", "INVALID_ADDRESS", &str, 1);
68                 free(addr);
69                 return false;
70         }
71         interface_add_address(state->iface, addr);
72         return true;
73 }
74
75 static int
76 parse_address_option(struct static_proto_state *state, struct uci_option *o, bool v6, int netmask)
77 {
78         struct uci_element *e;
79         int n_addr = 0;
80
81         if (o->type == UCI_TYPE_STRING) {
82                 n_addr++;
83                 if (!parse_addr(state, o->v.string, v6, netmask))
84                         return -1;
85         } else {
86                 uci_foreach_element(&o->v.list, e) {
87                         n_addr++;
88                         if (!parse_addr(state, e->name, v6, netmask))
89                                 return -1;
90                 }
91         }
92
93         return n_addr;
94 }
95
96 enum {
97         OPT_IPADDR,
98         OPT_IP6ADDR,
99         OPT_NETMASK,
100         OPT_GATEWAY,
101         OPT_IP6GW,
102         __OPT_MAX,
103 };
104
105 static const struct uci_parse_option opts[__OPT_MAX] = {
106         [OPT_IPADDR] = { .name = "ipaddr" },
107         [OPT_IP6ADDR] = { .name = "ip6addr" },
108         [OPT_NETMASK] = { .name = "netmask", .type = UCI_TYPE_STRING },
109         [OPT_GATEWAY] = { .name = "gateway", .type = UCI_TYPE_STRING },
110         [OPT_IP6GW] = { .name = "ip6gw", .type = UCI_TYPE_STRING },
111 };
112
113 static bool
114 static_proto_setup(struct static_proto_state *state)
115 {
116         struct uci_option *tb[__OPT_MAX];
117         struct in_addr ina;
118         const char *error;
119         int netmask = 32;
120         int n_v4 = 0, n_v6 = 0;
121
122         uci_parse_section(state->section, opts, __OPT_MAX, tb);
123
124         if (tb[OPT_NETMASK]) {
125                 if (!inet_aton(tb[OPT_NETMASK]->v.string, &ina)) {
126                         error = "INVALID_NETMASK";
127                         goto error;
128                 }
129
130                 netmask = 32 - fls(~(ntohl(ina.s_addr)));
131         }
132
133         if (tb[OPT_IPADDR])
134                 n_v4 = parse_address_option(state, tb[OPT_IPADDR], false, netmask);
135
136         if (tb[OPT_IP6ADDR])
137                 n_v6 = parse_address_option(state, tb[OPT_IP6ADDR], true, netmask);
138
139         if (!n_v4 && !n_v6) {
140                 error = "NO_ADDRESS";
141                 goto error;
142         }
143
144         if (n_v4 < 0 || n_v6 < 0)
145                 goto out;
146
147 #if 0
148         if (ps.n_v4 && tb[OPT_GATEWAY]) {
149                 if (!inet_pton(AF_INET, tb[OPT_GATEWAY]->v.string, &ps.ipv4gw)) {
150                         error = "INVALID_GATEWAY";
151                         goto error;
152                 }
153                 ps.flags |= STATIC_F_IPV4GW;
154         }
155
156         if (ps.n_v6 && tb[OPT_IP6GW]) {
157                 if (!inet_pton(AF_INET6, tb[OPT_IP6GW]->v.string, &ps.ipv6gw)) {
158                         error = "INVALID_GATEWAY";
159                         goto error;
160                 }
161                 ps.flags |= STATIC_F_IPV6GW;
162         }
163 #endif
164
165         return true;
166
167 error:
168         interface_add_error(state->iface, "proto-static", error, NULL, 0);
169 out:
170         return false;
171 }
172
173 static int
174 static_handler(struct interface_proto_state *proto,
175                enum interface_proto_cmd cmd, bool force)
176 {
177         struct static_proto_state *state;
178         int ret = 0;
179
180         state = container_of(proto, struct static_proto_state, proto);
181
182         switch (cmd) {
183         case PROTO_CMD_SETUP:
184                 if (static_proto_setup(state))
185                         break;
186
187                 /* fall through */
188         case PROTO_CMD_TEARDOWN:
189                 interface_del_ctx_addr(state->iface, state);
190                 break;
191         }
192         return ret;
193 }
194
195 static void
196 static_free(struct interface_proto_state *proto)
197 {
198         struct static_proto_state *state;
199
200         state = container_of(proto, struct static_proto_state, proto);
201         free(state);
202 }
203
204 struct interface_proto_state *
205 static_attach(struct proto_handler *h, struct interface *iface,
206               struct uci_section *s)
207 {
208         struct static_proto_state *state;
209
210         state = calloc(1, sizeof(*state));
211         state->iface = iface;
212         state->section = s;
213         state->proto.free = static_free;
214         state->proto.handler = static_handler;
215         state->proto.flags = PROTO_FLAG_IMMEDIATE;
216
217         return &state->proto;
218 }
219
220 static struct proto_handler static_proto = {
221         .name = "static",
222         .attach = static_attach,
223 };
224
225 static void __init
226 static_proto_init(void)
227 {
228         add_proto_handler(&static_proto);
229 }