1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (c) 2014-2015 Timo Teräs
10 #include <net/if_arp.h>
20 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_IF
, "NHRP interface");
21 DEFINE_MTYPE_STATIC(NHRPD
, NHRP_IF_GRE
, "NHRP GRE interface");
23 struct hash
*nhrp_gre_list
;
25 static void nhrp_interface_update_cache_config(struct interface
*ifp
,
29 static unsigned int nhrp_gre_info_key(const void *data
)
31 const struct nhrp_gre_info
*r
= data
;
36 static bool nhrp_gre_info_cmp(const void *data
, const void *key
)
38 const struct nhrp_gre_info
*a
= data
, *b
= key
;
40 if (a
->ifindex
== b
->ifindex
)
45 static void *nhrp_interface_gre_alloc(void *data
)
47 struct nhrp_gre_info
*a
;
48 struct nhrp_gre_info
*b
= data
;
50 a
= XMALLOC(MTYPE_NHRP_IF_GRE
, sizeof(struct nhrp_gre_info
));
51 memcpy(a
, b
, sizeof(struct nhrp_gre_info
));
55 struct nhrp_gre_info
*nhrp_gre_info_alloc(struct nhrp_gre_info
*p
)
57 struct nhrp_gre_info
*a
;
59 a
= (struct nhrp_gre_info
*)hash_get(nhrp_gre_list
, p
,
60 nhrp_interface_gre_alloc
);
64 static int nhrp_if_new_hook(struct interface
*ifp
)
66 struct nhrp_interface
*nifp
;
69 nifp
= XCALLOC(MTYPE_NHRP_IF
, sizeof(struct nhrp_interface
));
74 notifier_init(&nifp
->notifier_list
);
75 for (afi
= 0; afi
< AFI_MAX
; afi
++) {
76 struct nhrp_afi_data
*ad
= &nifp
->afi
[afi
];
77 ad
->holdtime
= NHRPD_DEFAULT_HOLDTIME
;
78 nhrp_nhslist_init(&ad
->nhslist_head
);
79 nhrp_mcastlist_init(&ad
->mcastlist_head
);
85 static int nhrp_if_delete_hook(struct interface
*ifp
)
87 struct nhrp_interface
*nifp
= ifp
->info
;
89 debugf(NHRP_DEBUG_IF
, "Deleted interface (%s)", ifp
->name
);
91 nhrp_cache_interface_del(ifp
);
92 nhrp_nhs_interface_del(ifp
);
93 nhrp_multicast_interface_del(ifp
);
94 nhrp_peer_interface_del(ifp
);
96 if (nifp
->ipsec_profile
)
97 free(nifp
->ipsec_profile
);
98 if (nifp
->ipsec_fallback_profile
)
99 free(nifp
->ipsec_fallback_profile
);
103 XFREE(MTYPE_NHRP_IF
, ifp
->info
);
107 void nhrp_interface_init(void)
109 hook_register_prio(if_add
, 0, nhrp_if_new_hook
);
110 hook_register_prio(if_del
, 0, nhrp_if_delete_hook
);
112 nhrp_gre_list
= hash_create(nhrp_gre_info_key
, nhrp_gre_info_cmp
,
113 "NHRP GRE list Hash");
116 void nhrp_interface_update_mtu(struct interface
*ifp
, afi_t afi
)
118 struct nhrp_interface
*nifp
= ifp
->info
;
119 struct nhrp_afi_data
*if_ad
= &nifp
->afi
[afi
];
120 unsigned short new_mtu
;
122 if (if_ad
->configured_mtu
< 0)
123 new_mtu
= nifp
->nbmaifp
? nifp
->nbmaifp
->mtu
: 0;
125 new_mtu
= if_ad
->configured_mtu
;
129 if (new_mtu
!= if_ad
->mtu
) {
130 debugf(NHRP_DEBUG_IF
, "%s: MTU changed to %d", ifp
->name
,
132 if_ad
->mtu
= new_mtu
;
133 notifier_call(&nifp
->notifier_list
,
134 NOTIFY_INTERFACE_MTU_CHANGED
);
138 static void nhrp_interface_update_source(struct interface
*ifp
)
140 struct nhrp_interface
*nifp
= ifp
->info
;
142 if (!nifp
->source
|| !nifp
->nbmaifp
143 || ((ifindex_t
)nifp
->link_idx
== nifp
->nbmaifp
->ifindex
144 && (nifp
->link_vrf_id
== nifp
->nbmaifp
->vrf
->vrf_id
)))
147 nifp
->link_idx
= nifp
->nbmaifp
->ifindex
;
148 nifp
->link_vrf_id
= nifp
->nbmaifp
->vrf
->vrf_id
;
149 debugf(NHRP_DEBUG_IF
, "%s: bound device index changed to %d, vr %u",
150 ifp
->name
, nifp
->link_idx
, nifp
->link_vrf_id
);
151 nhrp_send_zebra_gre_source_set(ifp
, nifp
->link_idx
, nifp
->link_vrf_id
);
154 static void nhrp_interface_interface_notifier(struct notifier_block
*n
,
157 struct nhrp_interface
*nifp
=
158 container_of(n
, struct nhrp_interface
, nbmanifp_notifier
);
159 struct interface
*nbmaifp
= nifp
->nbmaifp
;
160 struct nhrp_interface
*nbmanifp
= nbmaifp
->info
;
163 case NOTIFY_INTERFACE_CHANGED
:
164 nhrp_interface_update_nbma(nifp
->ifp
, NULL
);
166 case NOTIFY_INTERFACE_ADDRESS_CHANGED
:
167 nifp
->nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
168 nhrp_interface_update(nifp
->ifp
);
169 notifier_call(&nifp
->notifier_list
,
170 NOTIFY_INTERFACE_NBMA_CHANGED
);
171 debugf(NHRP_DEBUG_IF
, "%s: NBMA change: address %pSU",
172 nifp
->ifp
->name
, &nifp
->nbma
);
177 void nhrp_interface_update_nbma(struct interface
*ifp
,
178 struct nhrp_gre_info
*gre_info
)
180 struct nhrp_interface
*nifp
= ifp
->info
, *nbmanifp
= NULL
;
181 struct interface
*nbmaifp
= NULL
;
182 union sockunion nbma
;
183 struct in_addr saddr
= {0};
185 sockunion_family(&nbma
) = AF_UNSPEC
;
188 nbmaifp
= if_lookup_by_name(nifp
->source
, nifp
->link_vrf_id
);
190 if (ifp
->ll_type
!= ZEBRA_LLT_IPGRE
)
191 debugf(NHRP_DEBUG_IF
, "%s: Ignoring non GRE interface type %u",
192 __func__
, ifp
->ll_type
);
195 nhrp_send_zebra_gre_request(ifp
);
198 nifp
->i_grekey
= gre_info
->ikey
;
199 nifp
->o_grekey
= gre_info
->okey
;
200 nifp
->link_idx
= gre_info
->ifindex_link
;
201 nifp
->link_vrf_id
= gre_info
->vrfid_link
;
202 saddr
.s_addr
= gre_info
->vtep_ip
.s_addr
;
204 debugf(NHRP_DEBUG_IF
, "%s: GRE: %x %x %x", ifp
->name
,
205 nifp
->i_grekey
, nifp
->link_idx
, saddr
.s_addr
);
207 sockunion_set(&nbma
, AF_INET
,
208 (uint8_t *)&saddr
.s_addr
,
209 sizeof(saddr
.s_addr
));
210 else if (!nbmaifp
&& nifp
->link_idx
!= IFINDEX_INTERNAL
)
212 if_lookup_by_index(nifp
->link_idx
,
217 nbmanifp
= nbmaifp
->info
;
219 if (nbmaifp
!= nifp
->nbmaifp
) {
221 struct nhrp_interface
*prev_nifp
= nifp
->nbmaifp
->info
;
223 notifier_del(&nifp
->nbmanifp_notifier
,
224 &prev_nifp
->notifier_list
);
226 nifp
->nbmaifp
= nbmaifp
;
228 notifier_add(&nifp
->nbmanifp_notifier
,
229 &nbmanifp
->notifier_list
,
230 nhrp_interface_interface_notifier
);
231 debugf(NHRP_DEBUG_IF
, "%s: bound to %s", ifp
->name
,
237 if (sockunion_family(&nbma
) == AF_UNSPEC
)
238 nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
239 nhrp_interface_update_mtu(ifp
, AFI_IP
);
240 nhrp_interface_update_source(ifp
);
243 if (!sockunion_same(&nbma
, &nifp
->nbma
)) {
245 nhrp_interface_update(nifp
->ifp
);
246 debugf(NHRP_DEBUG_IF
, "%s: NBMA address changed", ifp
->name
);
247 notifier_call(&nifp
->notifier_list
,
248 NOTIFY_INTERFACE_NBMA_CHANGED
);
251 nhrp_interface_update(ifp
);
254 static void nhrp_interface_update_address(struct interface
*ifp
, afi_t afi
,
257 const int family
= afi2family(afi
);
258 struct nhrp_interface
*nifp
= ifp
->info
;
259 struct nhrp_afi_data
*if_ad
= &nifp
->afi
[afi
];
260 struct nhrp_cache
*nc
;
261 struct connected
*c
, *best
;
262 struct listnode
*cnode
;
263 union sockunion addr
;
264 char buf
[PREFIX_STRLEN
];
266 /* Select new best match preferring primary address */
268 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, cnode
, c
)) {
269 if (PREFIX_FAMILY(c
->address
) != family
)
275 if ((best
->flags
& ZEBRA_IFA_SECONDARY
)
276 && !(c
->flags
& ZEBRA_IFA_SECONDARY
)) {
280 if (!(best
->flags
& ZEBRA_IFA_SECONDARY
)
281 && (c
->flags
& ZEBRA_IFA_SECONDARY
))
283 if (best
->address
->prefixlen
> c
->address
->prefixlen
) {
287 if (best
->address
->prefixlen
< c
->address
->prefixlen
)
291 /* On NHRP interfaces a host prefix is required */
292 if (best
&& if_ad
->configured
293 && best
->address
->prefixlen
!= 8 * prefix_blen(best
->address
)) {
294 zlog_notice("%s: %pFX is not a host prefix", ifp
->name
,
299 /* Update address if it changed */
301 prefix2sockunion(best
->address
, &addr
);
303 memset(&addr
, 0, sizeof(addr
));
305 if (!force
&& sockunion_same(&if_ad
->addr
, &addr
))
308 if (sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
309 nc
= nhrp_cache_get(ifp
, &if_ad
->addr
, 0);
311 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, -1,
312 NULL
, 0, NULL
, NULL
);
315 debugf(NHRP_DEBUG_KERNEL
, "%s: IPv%d address changed to %s", ifp
->name
,
316 afi
== AFI_IP
? 4 : 6,
317 best
? prefix2str(best
->address
, buf
, sizeof(buf
)) : "(none)");
320 if (if_ad
->configured
&& sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
321 nc
= nhrp_cache_get(ifp
, &addr
, 1);
323 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, 0, NULL
,
327 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_ADDRESS_CHANGED
);
330 void nhrp_interface_update(struct interface
*ifp
)
332 struct nhrp_interface
*nifp
= ifp
->info
;
333 struct nhrp_afi_data
*if_ad
;
337 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_CHANGED
);
339 for (afi
= 0; afi
< AFI_MAX
; afi
++) {
340 if_ad
= &nifp
->afi
[afi
];
342 if (sockunion_family(&nifp
->nbma
) == AF_UNSPEC
343 || ifp
->ifindex
== IFINDEX_INTERNAL
|| !if_is_up(ifp
)
344 || !if_ad
->network_id
) {
345 if (if_ad
->configured
) {
346 if_ad
->configured
= 0;
347 nhrp_interface_update_address(ifp
, afi
, 1);
352 if (!if_ad
->configured
) {
353 os_configure_dmvpn(ifp
->ifindex
, ifp
->name
,
355 nhrp_send_zebra_configure_arp(ifp
, afi2family(afi
));
356 if_ad
->configured
= 1;
357 nhrp_interface_update_address(ifp
, afi
, 1);
363 if (enabled
!= nifp
->enabled
) {
364 nifp
->enabled
= enabled
;
365 notifier_call(&nifp
->notifier_list
,
366 enabled
? NOTIFY_INTERFACE_UP
367 : NOTIFY_INTERFACE_DOWN
);
371 int nhrp_ifp_create(struct interface
*ifp
)
373 debugf(NHRP_DEBUG_IF
, "if-add: %s, ifindex: %u, hw_type: %d %s",
374 ifp
->name
, ifp
->ifindex
, ifp
->ll_type
,
375 if_link_type_str(ifp
->ll_type
));
377 nhrp_interface_update_nbma(ifp
, NULL
);
382 int nhrp_ifp_destroy(struct interface
*ifp
)
384 debugf(NHRP_DEBUG_IF
, "if-delete: %s", ifp
->name
);
386 nhrp_interface_update_cache_config(ifp
, false, AF_INET
);
387 nhrp_interface_update_cache_config(ifp
, false, AF_INET6
);
388 nhrp_interface_update(ifp
);
398 static void interface_config_update_nhrp_map(struct nhrp_cache_config
*cc
,
401 struct map_ctx
*ctx
= data
;
402 struct interface
*ifp
= cc
->ifp
;
403 struct nhrp_cache
*c
;
404 union sockunion nbma_addr
;
406 if (sockunion_family(&cc
->remote_addr
) != ctx
->family
)
409 /* gre layer not ready */
410 if (ifp
->vrf
->vrf_id
== VRF_UNKNOWN
)
413 c
= nhrp_cache_get(ifp
, &cc
->remote_addr
, ctx
->enabled
? 1 : 0);
414 if (!c
&& !ctx
->enabled
)
420 nhrp_cache_update_binding(
422 nhrp_peer_get(ifp
, &nbma_addr
), 0, NULL
, NULL
);
431 if (cc
->type
== NHRP_CACHE_LOCAL
)
432 nhrp_cache_update_binding(c
, NHRP_CACHE_LOCAL
, 0, NULL
, 0,
435 nhrp_cache_update_binding(c
, NHRP_CACHE_STATIC
, 0,
436 nhrp_peer_get(ifp
, &cc
->nbma
), 0,
441 static void nhrp_interface_update_cache_config(struct interface
*ifp
, bool available
, uint8_t family
)
443 struct map_ctx mapctx
;
445 mapctx
= (struct map_ctx
){
449 nhrp_cache_config_foreach(ifp
, interface_config_update_nhrp_map
,
454 int nhrp_ifp_up(struct interface
*ifp
)
456 debugf(NHRP_DEBUG_IF
, "if-up: %s", ifp
->name
);
457 nhrp_interface_update_nbma(ifp
, NULL
);
462 int nhrp_ifp_down(struct interface
*ifp
)
464 debugf(NHRP_DEBUG_IF
, "if-down: %s", ifp
->name
);
465 nhrp_interface_update(ifp
);
470 int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS
)
472 struct connected
*ifc
;
474 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
478 debugf(NHRP_DEBUG_IF
, "if-addr-add: %s: %pFX", ifc
->ifp
->name
,
481 nhrp_interface_update_address(
482 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
483 nhrp_interface_update_cache_config(ifc
->ifp
, true, PREFIX_FAMILY(ifc
->address
));
487 int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS
)
489 struct connected
*ifc
;
491 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
495 debugf(NHRP_DEBUG_IF
, "if-addr-del: %s: %pFX", ifc
->ifp
->name
,
498 nhrp_interface_update_address(
499 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
500 connected_free(&ifc
);
505 void nhrp_interface_notify_add(struct interface
*ifp
, struct notifier_block
*n
,
508 struct nhrp_interface
*nifp
= ifp
->info
;
510 notifier_add(n
, &nifp
->notifier_list
, fn
);
513 void nhrp_interface_notify_del(struct interface
*ifp
, struct notifier_block
*n
)
515 struct nhrp_interface
*nifp
= ifp
->info
;
517 notifier_del(n
, &nifp
->notifier_list
);
520 void nhrp_interface_set_protection(struct interface
*ifp
, const char *profile
,
521 const char *fallback_profile
)
523 struct nhrp_interface
*nifp
= ifp
->info
;
525 if (nifp
->ipsec_profile
) {
526 vici_terminate_vc_by_profile_name(nifp
->ipsec_profile
);
528 free(nifp
->ipsec_profile
);
530 nifp
->ipsec_profile
= profile
? strdup(profile
) : NULL
;
532 if (nifp
->ipsec_fallback_profile
) {
533 vici_terminate_vc_by_profile_name(nifp
->ipsec_fallback_profile
);
535 free(nifp
->ipsec_fallback_profile
);
537 nifp
->ipsec_fallback_profile
=
538 fallback_profile
? strdup(fallback_profile
) : NULL
;
540 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_IPSEC_CHANGED
);
543 void nhrp_interface_set_source(struct interface
*ifp
, const char *ifname
)
545 struct nhrp_interface
*nifp
= ifp
->info
;
549 nifp
->source
= ifname
? strdup(ifname
) : NULL
;
551 nhrp_interface_update_nbma(ifp
, NULL
);