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 }