fix handling bridge interfaces with no initial device
[project/netifd.git] / proto.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 "interface-ip.h"
11 #include "proto.h"
12
13 static struct avl_tree handlers;
14
15 unsigned int
16 parse_netmask_string(const char *str, bool v6)
17 {
18         struct in_addr addr;
19         unsigned int ret;
20         char *err = NULL;
21
22         if (!strchr(str, '.')) {
23                 ret = strtoul(str, &err, 0);
24                 if (err && *err)
25                         goto error;
26
27                 return ret;
28         }
29
30         if (v6)
31                 goto error;
32
33         if (inet_aton(str, &addr) != 1)
34                 goto error;
35
36         return 32 - fls(~(ntohl(addr.s_addr)));
37
38 error:
39         return ~0;
40 }
41
42 static bool
43 split_netmask(char *str, unsigned int *netmask, bool v6)
44 {
45         char *delim = strchr(str, '/');
46
47         if (delim) {
48                 *(delim++) = 0;
49
50                 *netmask = parse_netmask_string(delim, v6);
51         }
52         return true;
53 }
54
55 static int
56 parse_ip_and_netmask(int af, const char *str, void *addr, unsigned int *netmask)
57 {
58         char *astr = alloca(strlen(str) + 1);
59
60         strcpy(astr, str);
61         if (!split_netmask(astr, netmask, af == AF_INET6))
62                 return 0;
63
64         if (af == AF_INET6) {
65                 if (*netmask > 128)
66                         return 0;
67         } else {
68                 if (*netmask > 32)
69                         return 0;
70         }
71
72         return inet_pton(af, astr, addr);
73 }
74
75 struct device_addr *
76 proto_parse_ip_addr_string(const char *str, bool v6, int mask)
77 {
78         struct device_addr *addr;
79         int af = v6 ? AF_INET6 : AF_INET;
80
81         addr = calloc(1, sizeof(*addr));
82         addr->flags = v6 ? DEVADDR_INET6 : DEVADDR_INET4;
83         addr->mask = mask;
84         if (!parse_ip_and_netmask(af, str, &addr->addr, &addr->mask)) {
85                 free(addr);
86                 return NULL;
87         }
88         return addr;
89 }
90
91
92 void add_proto_handler(struct proto_handler *p)
93 {
94         if (!handlers.comp)
95                 avl_init(&handlers, avl_strcmp, false, NULL);
96
97         if (p->avl.key)
98                 return;
99
100         p->avl.key = p->name;
101         avl_insert(&handlers, &p->avl);
102 }
103
104 static void
105 default_proto_free(struct interface_proto_state *proto)
106 {
107         free(proto);
108 }
109
110 static int
111 invalid_proto_handler(struct interface_proto_state *proto,
112                       enum interface_proto_cmd cmd, bool force)
113 {
114         return -1;
115 }
116
117 static int
118 no_proto_handler(struct interface_proto_state *proto,
119                  enum interface_proto_cmd cmd, bool force)
120 {
121         return 0;
122 }
123
124 static struct interface_proto_state *
125 default_proto_attach(const struct proto_handler *h,
126                      struct interface *iface, struct blob_attr *attr)
127 {
128         struct interface_proto_state *proto;
129
130         proto = calloc(1, sizeof(*proto));
131         proto->free = default_proto_free;
132         proto->cb = no_proto_handler;
133
134         return proto;
135 }
136
137 static const struct proto_handler no_proto = {
138         .name = "none",
139         .flags = PROTO_FLAG_IMMEDIATE,
140         .attach = default_proto_attach,
141 };
142
143 static const struct proto_handler *
144 get_proto_handler(const char *name)
145 {
146         struct proto_handler *proto;
147
148         if (!strcmp(name, "none"))
149             return &no_proto;
150
151         if (!handlers.comp)
152                 return NULL;
153
154         return avl_find_element(&handlers, name, proto, avl);
155 }
156
157 void
158 proto_init_interface(struct interface *iface, struct blob_attr *attr)
159 {
160         const struct proto_handler *proto = iface->proto_handler;
161         struct interface_proto_state *state = NULL;
162
163         if (proto)
164                 state = proto->attach(proto, iface, attr);
165
166         if (!state) {
167                 state = no_proto.attach(&no_proto, iface, attr);
168                 state->cb = invalid_proto_handler;
169         }
170
171         state->handler = proto;
172         interface_set_proto_state(iface, state);
173 }
174
175 void
176 proto_attach_interface(struct interface *iface, const char *proto_name)
177 {
178         const struct proto_handler *proto = NULL;
179
180         if (!proto_name) {
181                 interface_add_error(iface, "proto", "NO_PROTO", NULL, 0);
182                 return;
183         }
184
185         proto = get_proto_handler(proto_name);
186         if (!proto)
187                 interface_add_error(iface, "proto", "INVALID_PROTO", NULL, 0);
188
189         iface->proto_handler = proto;
190 }
191
192 int
193 interface_proto_event(struct interface_proto_state *proto,
194                       enum interface_proto_cmd cmd, bool force)
195 {
196         enum interface_proto_event ev;
197         int ret;
198
199         ret = proto->cb(proto, cmd, force);
200         if (ret || !(proto->handler->flags & PROTO_FLAG_IMMEDIATE))
201                 goto out;
202
203         switch(cmd) {
204         case PROTO_CMD_SETUP:
205                 ev = IFPEV_UP;
206                 break;
207         case PROTO_CMD_TEARDOWN:
208                 ev = IFPEV_DOWN;
209                 break;
210         default:
211                 return -EINVAL;
212         }
213         proto->proto_event(proto, ev);
214
215 out:
216         return ret;
217 }