2 * Copyright (c) 2014-2015 Timo Teräs
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.
14 #include <net/if_arp.h>
24 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_IF
, "NHRP interface");
25 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_IF_GRE
, "NHRP GRE interface");
27 struct hash
*nhrp_gre_list
;
29 static void nhrp_interface_update_cache_config(struct interface
*ifp
,
33 static unsigned int nhrp_gre_info_key(const void *data
)
35 const struct nhrp_gre_info
*r
= data
;
40 static bool nhrp_gre_info_cmp(const void *data
, const void *key
)
42 const struct nhrp_gre_info
*a
= data
, *b
= key
;
44 if (a
->ifindex
== b
->ifindex
)
49 static void *nhrp_interface_gre_alloc(void *data
)
51 struct nhrp_gre_info
*a
;
52 struct nhrp_gre_info
*b
= data
;
54 a
= XMALLOC(MTYPE_NHRP_IF_GRE
, sizeof(struct nhrp_gre_info
));
55 memcpy(a
, b
, sizeof(struct nhrp_gre_info
));
59 struct nhrp_gre_info
*nhrp_gre_info_alloc(struct nhrp_gre_info
*p
)
61 struct nhrp_gre_info
*a
;
63 a
= (struct nhrp_gre_info
*)hash_get(nhrp_gre_list
, p
,
64 nhrp_interface_gre_alloc
);
68 static int nhrp_if_new_hook(struct interface
*ifp
)
70 struct nhrp_interface
*nifp
;
73 nifp
= XCALLOC(MTYPE_NHRP_IF
, sizeof(struct nhrp_interface
));
78 notifier_init(&nifp
->notifier_list
);
79 for (afi
= 0; afi
< AFI_MAX
; afi
++) {
80 struct nhrp_afi_data
*ad
= &nifp
->afi
[afi
];
81 ad
->holdtime
= NHRPD_DEFAULT_HOLDTIME
;
82 nhrp_nhslist_init(&ad
->nhslist_head
);
83 nhrp_mcastlist_init(&ad
->mcastlist_head
);
89 static int nhrp_if_delete_hook(struct interface
*ifp
)
91 struct nhrp_interface
*nifp
= ifp
->info
;
93 debugf(NHRP_DEBUG_IF
, "Deleted interface (%s)", ifp
->name
);
95 nhrp_cache_interface_del(ifp
);
96 nhrp_nhs_interface_del(ifp
);
97 nhrp_multicast_interface_del(ifp
);
98 nhrp_peer_interface_del(ifp
);
100 if (nifp
->ipsec_profile
)
101 free(nifp
->ipsec_profile
);
102 if (nifp
->ipsec_fallback_profile
)
103 free(nifp
->ipsec_fallback_profile
);
107 XFREE(MTYPE_NHRP_IF
, ifp
->info
);
111 void nhrp_interface_init(void)
113 hook_register_prio(if_add
, 0, nhrp_if_new_hook
);
114 hook_register_prio(if_del
, 0, nhrp_if_delete_hook
);
116 nhrp_gre_list
= hash_create(nhrp_gre_info_key
, nhrp_gre_info_cmp
,
117 "NHRP GRE list Hash");
120 void nhrp_interface_update_mtu(struct interface
*ifp
, afi_t afi
)
122 struct nhrp_interface
*nifp
= ifp
->info
;
123 struct nhrp_afi_data
*if_ad
= &nifp
->afi
[afi
];
124 unsigned short new_mtu
;
126 if (if_ad
->configured_mtu
< 0)
127 new_mtu
= nifp
->nbmaifp
? nifp
->nbmaifp
->mtu
: 0;
129 new_mtu
= if_ad
->configured_mtu
;
133 if (new_mtu
!= if_ad
->mtu
) {
134 debugf(NHRP_DEBUG_IF
, "%s: MTU changed to %d", ifp
->name
,
136 if_ad
->mtu
= new_mtu
;
137 notifier_call(&nifp
->notifier_list
,
138 NOTIFY_INTERFACE_MTU_CHANGED
);
142 static void nhrp_interface_update_source(struct interface
*ifp
)
144 struct nhrp_interface
*nifp
= ifp
->info
;
146 if (!nifp
->source
|| !nifp
->nbmaifp
||
147 ((ifindex_t
)nifp
->link_idx
== nifp
->nbmaifp
->ifindex
&&
148 (nifp
->link_vrf_id
== nifp
->nbmaifp
->vrf_id
)))
151 nifp
->link_idx
= nifp
->nbmaifp
->ifindex
;
152 nifp
->link_vrf_id
= nifp
->nbmaifp
->vrf_id
;
153 debugf(NHRP_DEBUG_IF
, "%s: bound device index changed to %d, vr %u",
154 ifp
->name
, nifp
->link_idx
, nifp
->link_vrf_id
);
155 nhrp_send_zebra_gre_source_set(ifp
, nifp
->link_idx
, nifp
->link_vrf_id
);
158 static void nhrp_interface_interface_notifier(struct notifier_block
*n
,
161 struct nhrp_interface
*nifp
=
162 container_of(n
, struct nhrp_interface
, nbmanifp_notifier
);
163 struct interface
*nbmaifp
= nifp
->nbmaifp
;
164 struct nhrp_interface
*nbmanifp
= nbmaifp
->info
;
167 case NOTIFY_INTERFACE_CHANGED
:
168 nhrp_interface_update_mtu(nifp
->ifp
, AFI_IP
);
169 nhrp_interface_update_source(nifp
->ifp
);
171 case NOTIFY_INTERFACE_ADDRESS_CHANGED
:
172 nifp
->nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
173 nhrp_interface_update(nifp
->ifp
);
174 notifier_call(&nifp
->notifier_list
,
175 NOTIFY_INTERFACE_NBMA_CHANGED
);
176 debugf(NHRP_DEBUG_IF
, "%s: NBMA change: address %pSU",
177 nifp
->ifp
->name
, &nifp
->nbma
);
182 void nhrp_interface_update_nbma(struct interface
*ifp
,
183 struct nhrp_gre_info
*gre_info
)
185 struct nhrp_interface
*nifp
= ifp
->info
, *nbmanifp
= NULL
;
186 struct interface
*nbmaifp
= NULL
;
187 union sockunion nbma
;
189 sockunion_family(&nbma
) = AF_UNSPEC
;
192 nbmaifp
= if_lookup_by_name(nifp
->source
, nifp
->link_vrf_id
);
194 switch (ifp
->ll_type
) {
195 case ZEBRA_LLT_IPGRE
: {
196 struct in_addr saddr
= {0};
199 nhrp_send_zebra_gre_request(ifp
);
202 nifp
->i_grekey
= gre_info
->ikey
;
203 nifp
->o_grekey
= gre_info
->okey
;
204 nifp
->link_idx
= gre_info
->ifindex_link
;
205 nifp
->link_vrf_id
= gre_info
->vrfid_link
;
206 saddr
.s_addr
= gre_info
->vtep_ip
.s_addr
;
208 debugf(NHRP_DEBUG_IF
, "%s: GRE: %x %x %x", ifp
->name
,
209 nifp
->i_grekey
, nifp
->link_idx
, saddr
.s_addr
);
211 sockunion_set(&nbma
, AF_INET
,
212 (uint8_t *)&saddr
.s_addr
,
213 sizeof(saddr
.s_addr
));
214 else if (!nbmaifp
&& nifp
->link_idx
!= IFINDEX_INTERNAL
)
216 if_lookup_by_index(nifp
->link_idx
,
224 nbmanifp
= nbmaifp
->info
;
226 if (nbmaifp
!= nifp
->nbmaifp
) {
228 struct nhrp_interface
*prev_nifp
= nifp
->nbmaifp
->info
;
230 notifier_del(&nifp
->nbmanifp_notifier
,
231 &prev_nifp
->notifier_list
);
233 nifp
->nbmaifp
= nbmaifp
;
235 notifier_add(&nifp
->nbmanifp_notifier
,
236 &nbmanifp
->notifier_list
,
237 nhrp_interface_interface_notifier
);
238 debugf(NHRP_DEBUG_IF
, "%s: bound to %s", ifp
->name
,
244 if (sockunion_family(&nbma
) == AF_UNSPEC
)
245 nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
246 nhrp_interface_update_mtu(ifp
, AFI_IP
);
247 nhrp_interface_update_source(ifp
);
250 if (!sockunion_same(&nbma
, &nifp
->nbma
)) {
252 nhrp_interface_update(nifp
->ifp
);
253 debugf(NHRP_DEBUG_IF
, "%s: NBMA address changed", ifp
->name
);
254 notifier_call(&nifp
->notifier_list
,
255 NOTIFY_INTERFACE_NBMA_CHANGED
);
258 nhrp_interface_update(ifp
);
261 static void nhrp_interface_update_address(struct interface
*ifp
, afi_t afi
,
264 const int family
= afi2family(afi
);
265 struct nhrp_interface
*nifp
= ifp
->info
;
266 struct nhrp_afi_data
*if_ad
= &nifp
->afi
[afi
];
267 struct nhrp_cache
*nc
;
268 struct connected
*c
, *best
;
269 struct listnode
*cnode
;
270 union sockunion addr
;
271 char buf
[PREFIX_STRLEN
];
273 /* Select new best match preferring primary address */
275 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, cnode
, c
)) {
276 if (PREFIX_FAMILY(c
->address
) != family
)
282 if ((best
->flags
& ZEBRA_IFA_SECONDARY
)
283 && !(c
->flags
& ZEBRA_IFA_SECONDARY
)) {
287 if (!(best
->flags
& ZEBRA_IFA_SECONDARY
)
288 && (c
->flags
& ZEBRA_IFA_SECONDARY
))
290 if (best
->address
->prefixlen
> c
->address
->prefixlen
) {
294 if (best
->address
->prefixlen
< c
->address
->prefixlen
)
298 /* On NHRP interfaces a host prefix is required */
299 if (best
&& if_ad
->configured
300 && best
->address
->prefixlen
!= 8 * prefix_blen(best
->address
)) {
301 zlog_notice("%s: %pFX is not a host prefix", ifp
->name
,
306 /* Update address if it changed */
308 prefix2sockunion(best
->address
, &addr
);
310 memset(&addr
, 0, sizeof(addr
));
312 if (!force
&& sockunion_same(&if_ad
->addr
, &addr
))
315 if (sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
316 nc
= nhrp_cache_get(ifp
, &if_ad
->addr
, 0);
318 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, -1,
319 NULL
, 0, NULL
, NULL
);
322 debugf(NHRP_DEBUG_KERNEL
, "%s: IPv%d address changed to %s", ifp
->name
,
323 afi
== AFI_IP
? 4 : 6,
324 best
? prefix2str(best
->address
, buf
, sizeof(buf
)) : "(none)");
327 if (if_ad
->configured
&& sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
328 nc
= nhrp_cache_get(ifp
, &addr
, 1);
330 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, 0, NULL
,
334 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_ADDRESS_CHANGED
);
337 void nhrp_interface_update(struct interface
*ifp
)
339 struct nhrp_interface
*nifp
= ifp
->info
;
340 struct nhrp_afi_data
*if_ad
;
344 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_CHANGED
);
346 for (afi
= 0; afi
< AFI_MAX
; afi
++) {
347 if_ad
= &nifp
->afi
[afi
];
349 if (sockunion_family(&nifp
->nbma
) == AF_UNSPEC
350 || ifp
->ifindex
== IFINDEX_INTERNAL
|| !if_is_up(ifp
)
351 || !if_ad
->network_id
) {
352 if (if_ad
->configured
) {
353 if_ad
->configured
= 0;
354 nhrp_interface_update_address(ifp
, afi
, 1);
359 if (!if_ad
->configured
) {
360 os_configure_dmvpn(ifp
->ifindex
, ifp
->name
,
362 nhrp_send_zebra_configure_arp(ifp
, afi2family(afi
));
363 if_ad
->configured
= 1;
364 nhrp_interface_update_address(ifp
, afi
, 1);
370 if (enabled
!= nifp
->enabled
) {
371 nifp
->enabled
= enabled
;
372 notifier_call(&nifp
->notifier_list
,
373 enabled
? NOTIFY_INTERFACE_UP
374 : NOTIFY_INTERFACE_DOWN
);
378 int nhrp_ifp_create(struct interface
*ifp
)
380 debugf(NHRP_DEBUG_IF
, "if-add: %s, ifindex: %u, hw_type: %d %s",
381 ifp
->name
, ifp
->ifindex
, ifp
->ll_type
,
382 if_link_type_str(ifp
->ll_type
));
384 nhrp_interface_update_nbma(ifp
, NULL
);
389 int nhrp_ifp_destroy(struct interface
*ifp
)
391 debugf(NHRP_DEBUG_IF
, "if-delete: %s", ifp
->name
);
393 nhrp_interface_update_cache_config(ifp
, false, AF_INET
);
394 nhrp_interface_update_cache_config(ifp
, false, AF_INET6
);
395 nhrp_interface_update(ifp
);
405 static void interface_config_update_nhrp_map(struct nhrp_cache_config
*cc
,
408 struct map_ctx
*ctx
= data
;
409 struct interface
*ifp
= cc
->ifp
;
410 struct nhrp_cache
*c
;
411 union sockunion nbma_addr
;
413 if (sockunion_family(&cc
->remote_addr
) != ctx
->family
)
416 /* gre layer not ready */
417 if (ifp
->vrf_id
== VRF_UNKNOWN
)
420 c
= nhrp_cache_get(ifp
, &cc
->remote_addr
, ctx
->enabled
? 1 : 0);
421 if (!c
&& !ctx
->enabled
)
427 nhrp_cache_update_binding(
429 nhrp_peer_get(ifp
, &nbma_addr
), 0, NULL
, NULL
);
438 if (cc
->type
== NHRP_CACHE_LOCAL
)
439 nhrp_cache_update_binding(c
, NHRP_CACHE_LOCAL
, 0, NULL
, 0,
442 nhrp_cache_update_binding(c
, NHRP_CACHE_STATIC
, 0,
443 nhrp_peer_get(ifp
, &cc
->nbma
), 0,
448 static void nhrp_interface_update_cache_config(struct interface
*ifp
, bool available
, uint8_t family
)
450 struct map_ctx mapctx
;
452 mapctx
= (struct map_ctx
){
456 nhrp_cache_config_foreach(ifp
, interface_config_update_nhrp_map
,
461 int nhrp_ifp_up(struct interface
*ifp
)
463 debugf(NHRP_DEBUG_IF
, "if-up: %s", ifp
->name
);
464 nhrp_interface_update_nbma(ifp
, NULL
);
469 int nhrp_ifp_down(struct interface
*ifp
)
471 debugf(NHRP_DEBUG_IF
, "if-down: %s", ifp
->name
);
472 nhrp_interface_update(ifp
);
477 int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS
)
479 struct connected
*ifc
;
481 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
485 debugf(NHRP_DEBUG_IF
, "if-addr-add: %s: %pFX", ifc
->ifp
->name
,
488 nhrp_interface_update_address(
489 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
490 nhrp_interface_update_cache_config(ifc
->ifp
, true, PREFIX_FAMILY(ifc
->address
));
494 int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS
)
496 struct connected
*ifc
;
498 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
502 debugf(NHRP_DEBUG_IF
, "if-addr-del: %s: %pFX", ifc
->ifp
->name
,
505 nhrp_interface_update_address(
506 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
507 connected_free(&ifc
);
512 void nhrp_interface_notify_add(struct interface
*ifp
, struct notifier_block
*n
,
515 struct nhrp_interface
*nifp
= ifp
->info
;
517 notifier_add(n
, &nifp
->notifier_list
, fn
);
520 void nhrp_interface_notify_del(struct interface
*ifp
, struct notifier_block
*n
)
522 struct nhrp_interface
*nifp
= ifp
->info
;
524 notifier_del(n
, &nifp
->notifier_list
);
527 void nhrp_interface_set_protection(struct interface
*ifp
, const char *profile
,
528 const char *fallback_profile
)
530 struct nhrp_interface
*nifp
= ifp
->info
;
532 if (nifp
->ipsec_profile
) {
533 vici_terminate_vc_by_profile_name(nifp
->ipsec_profile
);
535 free(nifp
->ipsec_profile
);
537 nifp
->ipsec_profile
= profile
? strdup(profile
) : NULL
;
539 if (nifp
->ipsec_fallback_profile
) {
540 vici_terminate_vc_by_profile_name(nifp
->ipsec_fallback_profile
);
542 free(nifp
->ipsec_fallback_profile
);
544 nifp
->ipsec_fallback_profile
=
545 fallback_profile
? strdup(fallback_profile
) : NULL
;
547 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_IPSEC_CHANGED
);
550 void nhrp_interface_set_source(struct interface
*ifp
, const char *ifname
)
552 struct nhrp_interface
*nifp
= ifp
->info
;
556 nifp
->source
= ifname
? strdup(ifname
) : NULL
;
558 nhrp_interface_update_nbma(ifp
, NULL
);