source: src/router/quagga/nhrpd/nhrp_interface.c @ 31640

Last change on this file since 31640 was 31640, checked in by brainslayer, 4 months ago

update quagga

File size: 10.6 KB
Line 
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
20static 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
41static int nhrp_if_delete_hook(struct interface *ifp)
42{
43        XFREE(MTYPE_NHRP_IF, ifp->info);
44        return 0;
45}
46
47void 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
53void 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
73static 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
86static 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
109static 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
165static 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
233void 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
270int 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
288int 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
306int 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
321int 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
335int 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
354int 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
374void 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
380void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n)
381{
382        notifier_del(n);
383}
384
385void 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
396void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
397{
398        struct nhrp_interface *nifp = ifp->info;
399
400        if (nifp->source) free(nifp->source);
401        nifp->source = ifname ? strdup(ifname) : NULL;
402
403        nhrp_interface_update_nbma(ifp);
404}
Note: See TracBrowser for help on using the repository browser.