]> git.sommitrealweird.co.uk Git - quagga-debian.git/blob - nhrpd/nhrp_interface.c
New upstream version 1.2.4
[quagga-debian.git] / nhrpd / nhrp_interface.c
1 /* NHRP interface
2  * Copyright (c) 2014-2015 Timo Teräs
3  *
4  * This file is free software: you may copy, redistribute and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  */
9
10 #include <net/if_arp.h>
11 #include "zebra.h"
12 #include "linklist.h"
13 #include "memory.h"
14 #include "thread.h"
15
16 #include "nhrpd.h"
17 #include "os.h"
18 #include "netlink.h"
19
20 static int nhrp_if_new_hook(struct interface *ifp)
21 {
22         struct nhrp_interface *nifp;
23         afi_t afi;
24
25         nifp = XCALLOC(MTYPE_NHRP_IF, sizeof(struct nhrp_interface));
26         if (!nifp) return 0;
27
28         ifp->info = nifp;
29         nifp->ifp = ifp;
30
31         notifier_init(&nifp->notifier_list);
32         for (afi = 0; afi < AFI_MAX; afi++) {
33                 struct nhrp_afi_data *ad = &nifp->afi[afi];
34                 ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
35                 list_init(&ad->nhslist_head);
36         }
37
38         return 0;
39 }
40
41 static int nhrp_if_delete_hook(struct interface *ifp)
42 {
43         XFREE(MTYPE_NHRP_IF, ifp->info);
44         return 0;
45 }
46
47 void nhrp_interface_init(void)
48 {
49         if_add_hook(IF_NEW_HOOK,    nhrp_if_new_hook);
50         if_add_hook(IF_DELETE_HOOK, nhrp_if_delete_hook);
51 }
52
53 void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi)
54 {
55         struct nhrp_interface *nifp = ifp->info;
56         struct nhrp_afi_data *if_ad = &nifp->afi[afi];
57         unsigned short new_mtu;
58
59         if (if_ad->configured_mtu < 0)
60                 new_mtu = nifp->nbmaifp ? nifp->nbmaifp->mtu : 0;
61         else
62                 new_mtu = if_ad->configured_mtu;
63         if (new_mtu >= 1500)
64                 new_mtu = 0;
65
66         if (new_mtu != if_ad->mtu) {
67                 debugf(NHRP_DEBUG_IF, "%s: MTU changed to %d", ifp->name, new_mtu);
68                 if_ad->mtu = new_mtu;
69                 notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_MTU_CHANGED);
70         }
71 }
72
73 static void nhrp_interface_update_source(struct interface *ifp)
74 {
75         struct nhrp_interface *nifp = ifp->info;
76
77         if (!nifp->source || !nifp->nbmaifp ||
78             nifp->linkidx == nifp->nbmaifp->ifindex)
79                 return;
80
81         nifp->linkidx = nifp->nbmaifp->ifindex;
82         debugf(NHRP_DEBUG_IF, "%s: bound device index changed to %d", ifp->name, nifp->linkidx);
83         netlink_gre_set_link(ifp->ifindex, nifp->linkidx);
84 }
85
86 static void nhrp_interface_interface_notifier(struct notifier_block *n, unsigned long cmd)
87 {
88         struct nhrp_interface *nifp = container_of(n, struct nhrp_interface, nbmanifp_notifier);
89         struct interface *nbmaifp = nifp->nbmaifp;
90         struct nhrp_interface *nbmanifp = nbmaifp->info;
91         char buf[SU_ADDRSTRLEN];
92
93         switch (cmd) {
94         case NOTIFY_INTERFACE_CHANGED:
95                 nhrp_interface_update_mtu(nifp->ifp, AFI_IP);
96                 nhrp_interface_update_source(nifp->ifp);
97                 break;
98         case NOTIFY_INTERFACE_ADDRESS_CHANGED:
99                 nifp->nbma = nbmanifp->afi[AFI_IP].addr;
100                 nhrp_interface_update(nifp->ifp);
101                 notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
102                 debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %s",
103                         nifp->ifp->name,
104                         sockunion2str(&nifp->nbma, buf, sizeof buf));
105                 break;
106         }
107 }
108
109 static void nhrp_interface_update_nbma(struct interface *ifp)
110 {
111         struct nhrp_interface *nifp = ifp->info, *nbmanifp = NULL;
112         struct interface *nbmaifp = NULL;
113         union sockunion nbma;
114
115         sockunion_family(&nbma) = AF_UNSPEC;
116
117         if (nifp->source)
118                 nbmaifp = if_lookup_by_name(nifp->source);
119
120         switch (ifp->ll_type) {
121         case ZEBRA_LLT_IPGRE: {
122                         struct in_addr saddr = {0};
123                         netlink_gre_get_info(ifp->ifindex, &nifp->grekey, &nifp->linkidx, &saddr);
124                         debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name, nifp->grekey, nifp->linkidx, saddr.s_addr);
125                         if (saddr.s_addr)
126                                 sockunion_set(&nbma, AF_INET, (u_char *) &saddr.s_addr, sizeof(saddr.s_addr));
127                         else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL)
128                                 nbmaifp = if_lookup_by_index(nifp->linkidx);
129                 }
130                 break;
131         default:
132                 break;
133         }
134
135         if (nbmaifp)
136                 nbmanifp = nbmaifp->info;
137
138         if (nbmaifp != nifp->nbmaifp) {
139                 if (nifp->nbmaifp)
140                         notifier_del(&nifp->nbmanifp_notifier);
141                 nifp->nbmaifp = nbmaifp;
142                 if (nbmaifp) {
143                         notifier_add(&nifp->nbmanifp_notifier, &nbmanifp->notifier_list, nhrp_interface_interface_notifier);
144                         debugf(NHRP_DEBUG_IF, "%s: bound to %s", ifp->name, nbmaifp->name);
145                 }
146         }
147
148         if (nbmaifp) {
149                 if (sockunion_family(&nbma) == AF_UNSPEC)
150                         nbma = nbmanifp->afi[AFI_IP].addr;
151                 nhrp_interface_update_mtu(ifp, AFI_IP);
152                 nhrp_interface_update_source(ifp);
153         }
154
155         if (!sockunion_same(&nbma, &nifp->nbma)) {
156                 nifp->nbma = nbma;
157                 nhrp_interface_update(nifp->ifp);
158                 debugf(NHRP_DEBUG_IF, "%s: NBMA address changed", ifp->name);
159                 notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_NBMA_CHANGED);
160         }
161
162         nhrp_interface_update(ifp);
163 }
164
165 static void nhrp_interface_update_address(struct interface *ifp, afi_t afi, int force)
166 {
167         const int family = afi2family(afi);
168         struct nhrp_interface *nifp = ifp->info;
169         struct nhrp_afi_data *if_ad = &nifp->afi[afi];
170         struct nhrp_cache *nc;
171         struct connected *c, *best;
172         struct listnode *cnode;
173         union sockunion addr;
174         char buf[PREFIX_STRLEN];
175
176         /* Select new best match preferring primary address */
177         best = NULL;
178         for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
179                 if (PREFIX_FAMILY(c->address) != family)
180                         continue;
181                 if (best == NULL) {
182                         best = c;
183                         continue;
184                 }
185                 if ((best->flags & ZEBRA_IFA_SECONDARY) && !(c->flags & ZEBRA_IFA_SECONDARY)) {
186                         best = c;
187                         continue;
188                 }
189                 if (!(best->flags & ZEBRA_IFA_SECONDARY) && (c->flags & ZEBRA_IFA_SECONDARY))
190                         continue;
191                 if (best->address->prefixlen > c->address->prefixlen) {
192                         best = c;
193                         continue;
194                 }
195                 if (best->address->prefixlen < c->address->prefixlen)
196                         continue;
197         }
198
199         /* On NHRP interfaces a host prefix is required */
200         if (best && if_ad->configured && best->address->prefixlen != 8 * prefix_blen(best->address)) {
201                 zlog_notice("%s: %s is not a host prefix", ifp->name,
202                         prefix2str(best->address, buf, sizeof buf));
203                 best = NULL;
204         }
205
206         /* Update address if it changed */
207         if (best)
208                 prefix2sockunion(best->address, &addr);
209         else
210                 memset(&addr, 0, sizeof(addr));
211
212         if (!force && sockunion_same(&if_ad->addr, &addr))
213                 return;
214
215         if (sockunion_family(&if_ad->addr) != AF_UNSPEC) {
216                 nc = nhrp_cache_get(ifp, &if_ad->addr, 0);
217                 if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1, NULL, 0, NULL);
218         }
219
220         debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s",
221                 ifp->name, afi == AFI_IP ? 4 : 6,
222                 best ? prefix2str(best->address, buf, sizeof buf) : "(none)");
223         if_ad->addr = addr;
224
225         if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) {
226                 nc = nhrp_cache_get(ifp, &addr, 1);
227                 if (nc) nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL, 0, NULL);
228         }
229
230         notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
231 }
232
233 void nhrp_interface_update(struct interface *ifp)
234 {
235         struct nhrp_interface *nifp = ifp->info;
236         struct nhrp_afi_data *if_ad;
237         afi_t afi;
238         int enabled = 0;
239
240         notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_CHANGED);
241
242         for (afi = 0; afi < AFI_MAX; afi++) {
243                 if_ad = &nifp->afi[afi];
244
245                 if (sockunion_family(&nifp->nbma) == AF_UNSPEC ||
246                     ifp->ifindex == IFINDEX_INTERNAL || !if_is_up(ifp) ||
247                     !if_ad->network_id) {
248                         if (if_ad->configured) {
249                                 if_ad->configured = 0;
250                                 nhrp_interface_update_address(ifp, afi, 1);
251                         }
252                         continue;
253                 }
254
255                 if (!if_ad->configured) {
256                         os_configure_dmvpn(ifp->ifindex, ifp->name, afi2family(afi));
257                         if_ad->configured = 1;
258                         nhrp_interface_update_address(ifp, afi, 1);
259                 }
260
261                 enabled = 1;
262         }
263
264         if (enabled != nifp->enabled) {
265                 nifp->enabled = enabled;
266                 notifier_call(&nifp->notifier_list, enabled ? NOTIFY_INTERFACE_UP : NOTIFY_INTERFACE_DOWN);
267         }
268 }
269
270 int nhrp_interface_add(int cmd, struct zclient *client, zebra_size_t length, vrf_id_t vrf_id)
271 {
272         struct interface *ifp;
273
274         /* read and add the interface in the iflist. */
275         ifp = zebra_interface_add_read(client->ibuf, vrf_id);
276         if (ifp == NULL)
277                 return 0;
278
279         debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s",
280                 ifp->name, ifp->ifindex,
281                 ifp->ll_type, if_link_type_str(ifp->ll_type));
282
283         nhrp_interface_update_nbma(ifp);
284
285         return 0;
286 }
287
288 int nhrp_interface_delete(int cmd, struct zclient *client,
289                           zebra_size_t length, vrf_id_t vrf_id)
290 {
291         struct interface *ifp;
292         struct stream *s;
293
294         s = client->ibuf;
295         ifp = zebra_interface_state_read(s, vrf_id);
296         if (ifp == NULL)
297                 return 0;
298
299         debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name);
300         ifp->ifindex = IFINDEX_INTERNAL;
301         nhrp_interface_update(ifp);
302         /* if_delete(ifp); */
303         return 0;
304 }
305
306 int nhrp_interface_up(int cmd, struct zclient *client,
307                       zebra_size_t length, vrf_id_t vrf_id)
308 {
309         struct interface *ifp;
310
311         ifp = zebra_interface_state_read(client->ibuf, vrf_id);
312         if (ifp == NULL)
313                 return 0;
314
315         debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name);
316         nhrp_interface_update_nbma(ifp);
317
318         return 0;
319 }
320
321 int nhrp_interface_down(int cmd, struct zclient *client,
322                         zebra_size_t length, vrf_id_t vrf_id)
323 {
324         struct interface *ifp;
325
326         ifp = zebra_interface_state_read(client->ibuf, vrf_id);
327         if (ifp == NULL)
328                 return 0;
329
330         debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name);
331         nhrp_interface_update(ifp);
332         return 0;
333 }
334
335 int nhrp_interface_address_add(int cmd, struct zclient *client,
336                                zebra_size_t length, vrf_id_t vrf_id)
337 {
338         struct connected *ifc;
339         char buf[PREFIX_STRLEN];
340
341         ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
342         if (ifc == NULL)
343                 return 0;
344
345         debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %s",
346                 ifc->ifp->name,
347                 prefix2str(ifc->address, buf, sizeof buf));
348
349         nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
350
351         return 0;
352 }
353
354 int nhrp_interface_address_delete(int cmd, struct zclient *client,
355                                   zebra_size_t length, vrf_id_t vrf_id)
356 {
357         struct connected *ifc;
358         char buf[PREFIX_STRLEN];
359
360         ifc = zebra_interface_address_read(cmd, client->ibuf, vrf_id);
361         if (ifc == NULL)
362                 return 0;
363
364         debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %s",
365                 ifc->ifp->name,
366                 prefix2str(ifc->address, buf, sizeof buf));
367
368         nhrp_interface_update_address(ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
369         connected_free(ifc);
370
371         return 0;
372 }
373
374 void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n, notifier_fn_t fn)
375 {
376         struct nhrp_interface *nifp = ifp->info;
377         notifier_add(n, &nifp->notifier_list, fn);
378 }
379
380 void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n)
381 {
382         notifier_del(n);
383 }
384
385 void nhrp_interface_set_protection(struct interface *ifp, const char *profile, const char *fallback_profile)
386 {
387         struct nhrp_interface *nifp = ifp->info;
388
389         if (nifp->ipsec_profile) free(nifp->ipsec_profile);
390         nifp->ipsec_profile = profile ? strdup(profile) : NULL;
391
392         if (nifp->ipsec_fallback_profile) free(nifp->ipsec_fallback_profile);
393         nifp->ipsec_fallback_profile = fallback_profile ? strdup(fallback_profile) : NULL;
394
395         notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
396 }
397
398 void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
399 {
400         struct nhrp_interface *nifp = ifp->info;
401
402         if (nifp->source) free(nifp->source);
403         nifp->source = ifname ? strdup(ifname) : NULL;
404
405         nhrp_interface_update_nbma(ifp);
406 }