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
->vrf_id
)))
151 nifp
->link_idx
= nifp
->nbmaifp
->ifindex
;
152 nifp
->link_vrf_id
= nifp
->nbmaifp
->vrf
->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_nbma(nifp
->ifp
, NULL
);
170 case NOTIFY_INTERFACE_ADDRESS_CHANGED
:
171 nifp
->nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
172 nhrp_interface_update(nifp
->ifp
);
173 notifier_call(&nifp
->notifier_list
,
174 NOTIFY_INTERFACE_NBMA_CHANGED
);
175 debugf(NHRP_DEBUG_IF
, "%s: NBMA change: address %pSU",
176 nifp
->ifp
->name
, &nifp
->nbma
);
181 void nhrp_interface_update_nbma(struct interface
*ifp
,
182 struct nhrp_gre_info
*gre_info
)
184 struct nhrp_interface
*nifp
= ifp
->info
, *nbmanifp
= NULL
;
185 struct interface
*nbmaifp
= NULL
;
186 union sockunion nbma
;
188 sockunion_family(&nbma
) = AF_UNSPEC
;
191 nbmaifp
= if_lookup_by_name(nifp
->source
, nifp
->link_vrf_id
);
193 switch (ifp
->ll_type
) {
194 case ZEBRA_LLT_IPGRE
: {
195 struct in_addr saddr
= {0};
198 nhrp_send_zebra_gre_request(ifp
);
201 nifp
->i_grekey
= gre_info
->ikey
;
202 nifp
->o_grekey
= gre_info
->okey
;
203 nifp
->link_idx
= gre_info
->ifindex_link
;
204 nifp
->link_vrf_id
= gre_info
->vrfid_link
;
205 saddr
.s_addr
= gre_info
->vtep_ip
.s_addr
;
207 debugf(NHRP_DEBUG_IF
, "%s: GRE: %x %x %x", ifp
->name
,
208 nifp
->i_grekey
, nifp
->link_idx
, saddr
.s_addr
);
210 sockunion_set(&nbma
, AF_INET
,
211 (uint8_t *)&saddr
.s_addr
,
212 sizeof(saddr
.s_addr
));
213 else if (!nbmaifp
&& nifp
->link_idx
!= IFINDEX_INTERNAL
)
215 if_lookup_by_index(nifp
->link_idx
,
223 nbmanifp
= nbmaifp
->info
;
225 if (nbmaifp
!= nifp
->nbmaifp
) {
227 struct nhrp_interface
*prev_nifp
= nifp
->nbmaifp
->info
;
229 notifier_del(&nifp
->nbmanifp_notifier
,
230 &prev_nifp
->notifier_list
);
232 nifp
->nbmaifp
= nbmaifp
;
234 notifier_add(&nifp
->nbmanifp_notifier
,
235 &nbmanifp
->notifier_list
,
236 nhrp_interface_interface_notifier
);
237 debugf(NHRP_DEBUG_IF
, "%s: bound to %s", ifp
->name
,
243 if (sockunion_family(&nbma
) == AF_UNSPEC
)
244 nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
245 nhrp_interface_update_mtu(ifp
, AFI_IP
);
246 nhrp_interface_update_source(ifp
);
249 if (!sockunion_same(&nbma
, &nifp
->nbma
)) {
251 nhrp_interface_update(nifp
->ifp
);
252 debugf(NHRP_DEBUG_IF
, "%s: NBMA address changed", ifp
->name
);
253 notifier_call(&nifp
->notifier_list
,
254 NOTIFY_INTERFACE_NBMA_CHANGED
);
257 nhrp_interface_update(ifp
);
260 static void nhrp_interface_update_address(struct interface
*ifp
, afi_t afi
,
263 const int family
= afi2family(afi
);
264 struct nhrp_interface
*nifp
= ifp
->info
;
265 struct nhrp_afi_data
*if_ad
= &nifp
->afi
[afi
];
266 struct nhrp_cache
*nc
;
267 struct connected
*c
, *best
;
268 struct listnode
*cnode
;
269 union sockunion addr
;
270 char buf
[PREFIX_STRLEN
];
272 /* Select new best match preferring primary address */
274 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, cnode
, c
)) {
275 if (PREFIX_FAMILY(c
->address
) != family
)
281 if ((best
->flags
& ZEBRA_IFA_SECONDARY
)
282 && !(c
->flags
& ZEBRA_IFA_SECONDARY
)) {
286 if (!(best
->flags
& ZEBRA_IFA_SECONDARY
)
287 && (c
->flags
& ZEBRA_IFA_SECONDARY
))
289 if (best
->address
->prefixlen
> c
->address
->prefixlen
) {
293 if (best
->address
->prefixlen
< c
->address
->prefixlen
)
297 /* On NHRP interfaces a host prefix is required */
298 if (best
&& if_ad
->configured
299 && best
->address
->prefixlen
!= 8 * prefix_blen(best
->address
)) {
300 zlog_notice("%s: %pFX is not a host prefix", ifp
->name
,
305 /* Update address if it changed */
307 prefix2sockunion(best
->address
, &addr
);
309 memset(&addr
, 0, sizeof(addr
));
311 if (!force
&& sockunion_same(&if_ad
->addr
, &addr
))
314 if (sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
315 nc
= nhrp_cache_get(ifp
, &if_ad
->addr
, 0);
317 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, -1,
318 NULL
, 0, NULL
, NULL
);
321 debugf(NHRP_DEBUG_KERNEL
, "%s: IPv%d address changed to %s", ifp
->name
,
322 afi
== AFI_IP
? 4 : 6,
323 best
? prefix2str(best
->address
, buf
, sizeof(buf
)) : "(none)");
326 if (if_ad
->configured
&& sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
327 nc
= nhrp_cache_get(ifp
, &addr
, 1);
329 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, 0, NULL
,
333 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_ADDRESS_CHANGED
);
336 void nhrp_interface_update(struct interface
*ifp
)
338 struct nhrp_interface
*nifp
= ifp
->info
;
339 struct nhrp_afi_data
*if_ad
;
343 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_CHANGED
);
345 for (afi
= 0; afi
< AFI_MAX
; afi
++) {
346 if_ad
= &nifp
->afi
[afi
];
348 if (sockunion_family(&nifp
->nbma
) == AF_UNSPEC
349 || ifp
->ifindex
== IFINDEX_INTERNAL
|| !if_is_up(ifp
)
350 || !if_ad
->network_id
) {
351 if (if_ad
->configured
) {
352 if_ad
->configured
= 0;
353 nhrp_interface_update_address(ifp
, afi
, 1);
358 if (!if_ad
->configured
) {
359 os_configure_dmvpn(ifp
->ifindex
, ifp
->name
,
361 nhrp_send_zebra_configure_arp(ifp
, afi2family(afi
));
362 if_ad
->configured
= 1;
363 nhrp_interface_update_address(ifp
, afi
, 1);
369 if (enabled
!= nifp
->enabled
) {
370 nifp
->enabled
= enabled
;
371 notifier_call(&nifp
->notifier_list
,
372 enabled
? NOTIFY_INTERFACE_UP
373 : NOTIFY_INTERFACE_DOWN
);
377 int nhrp_ifp_create(struct interface
*ifp
)
379 debugf(NHRP_DEBUG_IF
, "if-add: %s, ifindex: %u, hw_type: %d %s",
380 ifp
->name
, ifp
->ifindex
, ifp
->ll_type
,
381 if_link_type_str(ifp
->ll_type
));
383 nhrp_interface_update_nbma(ifp
, NULL
);
388 int nhrp_ifp_destroy(struct interface
*ifp
)
390 debugf(NHRP_DEBUG_IF
, "if-delete: %s", ifp
->name
);
392 nhrp_interface_update_cache_config(ifp
, false, AF_INET
);
393 nhrp_interface_update_cache_config(ifp
, false, AF_INET6
);
394 nhrp_interface_update(ifp
);
404 static void interface_config_update_nhrp_map(struct nhrp_cache_config
*cc
,
407 struct map_ctx
*ctx
= data
;
408 struct interface
*ifp
= cc
->ifp
;
409 struct nhrp_cache
*c
;
410 union sockunion nbma_addr
;
412 if (sockunion_family(&cc
->remote_addr
) != ctx
->family
)
415 /* gre layer not ready */
416 if (ifp
->vrf
->vrf_id
== VRF_UNKNOWN
)
419 c
= nhrp_cache_get(ifp
, &cc
->remote_addr
, ctx
->enabled
? 1 : 0);
420 if (!c
&& !ctx
->enabled
)
426 nhrp_cache_update_binding(
428 nhrp_peer_get(ifp
, &nbma_addr
), 0, NULL
, NULL
);
437 if (cc
->type
== NHRP_CACHE_LOCAL
)
438 nhrp_cache_update_binding(c
, NHRP_CACHE_LOCAL
, 0, NULL
, 0,
441 nhrp_cache_update_binding(c
, NHRP_CACHE_STATIC
, 0,
442 nhrp_peer_get(ifp
, &cc
->nbma
), 0,
447 static void nhrp_interface_update_cache_config(struct interface
*ifp
, bool available
, uint8_t family
)
449 struct map_ctx mapctx
;
451 mapctx
= (struct map_ctx
){
455 nhrp_cache_config_foreach(ifp
, interface_config_update_nhrp_map
,
460 int nhrp_ifp_up(struct interface
*ifp
)
462 debugf(NHRP_DEBUG_IF
, "if-up: %s", ifp
->name
);
463 nhrp_interface_update_nbma(ifp
, NULL
);
468 int nhrp_ifp_down(struct interface
*ifp
)
470 debugf(NHRP_DEBUG_IF
, "if-down: %s", ifp
->name
);
471 nhrp_interface_update(ifp
);
476 int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS
)
478 struct connected
*ifc
;
480 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
484 debugf(NHRP_DEBUG_IF
, "if-addr-add: %s: %pFX", ifc
->ifp
->name
,
487 nhrp_interface_update_address(
488 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
489 nhrp_interface_update_cache_config(ifc
->ifp
, true, PREFIX_FAMILY(ifc
->address
));
493 int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS
)
495 struct connected
*ifc
;
497 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
501 debugf(NHRP_DEBUG_IF
, "if-addr-del: %s: %pFX", ifc
->ifp
->name
,
504 nhrp_interface_update_address(
505 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
506 connected_free(&ifc
);
511 void nhrp_interface_notify_add(struct interface
*ifp
, struct notifier_block
*n
,
514 struct nhrp_interface
*nifp
= ifp
->info
;
516 notifier_add(n
, &nifp
->notifier_list
, fn
);
519 void nhrp_interface_notify_del(struct interface
*ifp
, struct notifier_block
*n
)
521 struct nhrp_interface
*nifp
= ifp
->info
;
523 notifier_del(n
, &nifp
->notifier_list
);
526 void nhrp_interface_set_protection(struct interface
*ifp
, const char *profile
,
527 const char *fallback_profile
)
529 struct nhrp_interface
*nifp
= ifp
->info
;
531 if (nifp
->ipsec_profile
) {
532 vici_terminate_vc_by_profile_name(nifp
->ipsec_profile
);
534 free(nifp
->ipsec_profile
);
536 nifp
->ipsec_profile
= profile
? strdup(profile
) : NULL
;
538 if (nifp
->ipsec_fallback_profile
) {
539 vici_terminate_vc_by_profile_name(nifp
->ipsec_fallback_profile
);
541 free(nifp
->ipsec_fallback_profile
);
543 nifp
->ipsec_fallback_profile
=
544 fallback_profile
? strdup(fallback_profile
) : NULL
;
546 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_IPSEC_CHANGED
);
549 void nhrp_interface_set_source(struct interface
*ifp
, const char *ifname
)
551 struct nhrp_interface
*nifp
= ifp
->info
;
555 nifp
->source
= ifname
? strdup(ifname
) : NULL
;
557 nhrp_interface_update_nbma(ifp
, NULL
);