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
;
187 struct in_addr saddr
= {0};
189 sockunion_family(&nbma
) = AF_UNSPEC
;
192 nbmaifp
= if_lookup_by_name(nifp
->source
, nifp
->link_vrf_id
);
194 if (ifp
->ll_type
!= ZEBRA_LLT_IPGRE
)
195 debugf(NHRP_DEBUG_IF
, "%s: Ignoring non GRE interface type %u",
196 __func__
, ifp
->ll_type
);
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
,
221 nbmanifp
= nbmaifp
->info
;
223 if (nbmaifp
!= nifp
->nbmaifp
) {
225 struct nhrp_interface
*prev_nifp
= nifp
->nbmaifp
->info
;
227 notifier_del(&nifp
->nbmanifp_notifier
,
228 &prev_nifp
->notifier_list
);
230 nifp
->nbmaifp
= nbmaifp
;
232 notifier_add(&nifp
->nbmanifp_notifier
,
233 &nbmanifp
->notifier_list
,
234 nhrp_interface_interface_notifier
);
235 debugf(NHRP_DEBUG_IF
, "%s: bound to %s", ifp
->name
,
241 if (sockunion_family(&nbma
) == AF_UNSPEC
)
242 nbma
= nbmanifp
->afi
[AFI_IP
].addr
;
243 nhrp_interface_update_mtu(ifp
, AFI_IP
);
244 nhrp_interface_update_source(ifp
);
247 if (!sockunion_same(&nbma
, &nifp
->nbma
)) {
249 nhrp_interface_update(nifp
->ifp
);
250 debugf(NHRP_DEBUG_IF
, "%s: NBMA address changed", ifp
->name
);
251 notifier_call(&nifp
->notifier_list
,
252 NOTIFY_INTERFACE_NBMA_CHANGED
);
255 nhrp_interface_update(ifp
);
258 static void nhrp_interface_update_address(struct interface
*ifp
, afi_t afi
,
261 const int family
= afi2family(afi
);
262 struct nhrp_interface
*nifp
= ifp
->info
;
263 struct nhrp_afi_data
*if_ad
= &nifp
->afi
[afi
];
264 struct nhrp_cache
*nc
;
265 struct connected
*c
, *best
;
266 struct listnode
*cnode
;
267 union sockunion addr
;
268 char buf
[PREFIX_STRLEN
];
270 /* Select new best match preferring primary address */
272 for (ALL_LIST_ELEMENTS_RO(ifp
->connected
, cnode
, c
)) {
273 if (PREFIX_FAMILY(c
->address
) != family
)
279 if ((best
->flags
& ZEBRA_IFA_SECONDARY
)
280 && !(c
->flags
& ZEBRA_IFA_SECONDARY
)) {
284 if (!(best
->flags
& ZEBRA_IFA_SECONDARY
)
285 && (c
->flags
& ZEBRA_IFA_SECONDARY
))
287 if (best
->address
->prefixlen
> c
->address
->prefixlen
) {
291 if (best
->address
->prefixlen
< c
->address
->prefixlen
)
295 /* On NHRP interfaces a host prefix is required */
296 if (best
&& if_ad
->configured
297 && best
->address
->prefixlen
!= 8 * prefix_blen(best
->address
)) {
298 zlog_notice("%s: %pFX is not a host prefix", ifp
->name
,
303 /* Update address if it changed */
305 prefix2sockunion(best
->address
, &addr
);
307 memset(&addr
, 0, sizeof(addr
));
309 if (!force
&& sockunion_same(&if_ad
->addr
, &addr
))
312 if (sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
313 nc
= nhrp_cache_get(ifp
, &if_ad
->addr
, 0);
315 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, -1,
316 NULL
, 0, NULL
, NULL
);
319 debugf(NHRP_DEBUG_KERNEL
, "%s: IPv%d address changed to %s", ifp
->name
,
320 afi
== AFI_IP
? 4 : 6,
321 best
? prefix2str(best
->address
, buf
, sizeof(buf
)) : "(none)");
324 if (if_ad
->configured
&& sockunion_family(&if_ad
->addr
) != AF_UNSPEC
) {
325 nc
= nhrp_cache_get(ifp
, &addr
, 1);
327 nhrp_cache_update_binding(nc
, NHRP_CACHE_LOCAL
, 0, NULL
,
331 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_ADDRESS_CHANGED
);
334 void nhrp_interface_update(struct interface
*ifp
)
336 struct nhrp_interface
*nifp
= ifp
->info
;
337 struct nhrp_afi_data
*if_ad
;
341 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_CHANGED
);
343 for (afi
= 0; afi
< AFI_MAX
; afi
++) {
344 if_ad
= &nifp
->afi
[afi
];
346 if (sockunion_family(&nifp
->nbma
) == AF_UNSPEC
347 || ifp
->ifindex
== IFINDEX_INTERNAL
|| !if_is_up(ifp
)
348 || !if_ad
->network_id
) {
349 if (if_ad
->configured
) {
350 if_ad
->configured
= 0;
351 nhrp_interface_update_address(ifp
, afi
, 1);
356 if (!if_ad
->configured
) {
357 os_configure_dmvpn(ifp
->ifindex
, ifp
->name
,
359 nhrp_send_zebra_configure_arp(ifp
, afi2family(afi
));
360 if_ad
->configured
= 1;
361 nhrp_interface_update_address(ifp
, afi
, 1);
367 if (enabled
!= nifp
->enabled
) {
368 nifp
->enabled
= enabled
;
369 notifier_call(&nifp
->notifier_list
,
370 enabled
? NOTIFY_INTERFACE_UP
371 : NOTIFY_INTERFACE_DOWN
);
375 int nhrp_ifp_create(struct interface
*ifp
)
377 debugf(NHRP_DEBUG_IF
, "if-add: %s, ifindex: %u, hw_type: %d %s",
378 ifp
->name
, ifp
->ifindex
, ifp
->ll_type
,
379 if_link_type_str(ifp
->ll_type
));
381 nhrp_interface_update_nbma(ifp
, NULL
);
386 int nhrp_ifp_destroy(struct interface
*ifp
)
388 debugf(NHRP_DEBUG_IF
, "if-delete: %s", ifp
->name
);
390 nhrp_interface_update_cache_config(ifp
, false, AF_INET
);
391 nhrp_interface_update_cache_config(ifp
, false, AF_INET6
);
392 nhrp_interface_update(ifp
);
402 static void interface_config_update_nhrp_map(struct nhrp_cache_config
*cc
,
405 struct map_ctx
*ctx
= data
;
406 struct interface
*ifp
= cc
->ifp
;
407 struct nhrp_cache
*c
;
408 union sockunion nbma_addr
;
410 if (sockunion_family(&cc
->remote_addr
) != ctx
->family
)
413 /* gre layer not ready */
414 if (ifp
->vrf
->vrf_id
== VRF_UNKNOWN
)
417 c
= nhrp_cache_get(ifp
, &cc
->remote_addr
, ctx
->enabled
? 1 : 0);
418 if (!c
&& !ctx
->enabled
)
424 nhrp_cache_update_binding(
426 nhrp_peer_get(ifp
, &nbma_addr
), 0, NULL
, NULL
);
435 if (cc
->type
== NHRP_CACHE_LOCAL
)
436 nhrp_cache_update_binding(c
, NHRP_CACHE_LOCAL
, 0, NULL
, 0,
439 nhrp_cache_update_binding(c
, NHRP_CACHE_STATIC
, 0,
440 nhrp_peer_get(ifp
, &cc
->nbma
), 0,
445 static void nhrp_interface_update_cache_config(struct interface
*ifp
, bool available
, uint8_t family
)
447 struct map_ctx mapctx
;
449 mapctx
= (struct map_ctx
){
453 nhrp_cache_config_foreach(ifp
, interface_config_update_nhrp_map
,
458 int nhrp_ifp_up(struct interface
*ifp
)
460 debugf(NHRP_DEBUG_IF
, "if-up: %s", ifp
->name
);
461 nhrp_interface_update_nbma(ifp
, NULL
);
466 int nhrp_ifp_down(struct interface
*ifp
)
468 debugf(NHRP_DEBUG_IF
, "if-down: %s", ifp
->name
);
469 nhrp_interface_update(ifp
);
474 int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS
)
476 struct connected
*ifc
;
478 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
482 debugf(NHRP_DEBUG_IF
, "if-addr-add: %s: %pFX", ifc
->ifp
->name
,
485 nhrp_interface_update_address(
486 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
487 nhrp_interface_update_cache_config(ifc
->ifp
, true, PREFIX_FAMILY(ifc
->address
));
491 int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS
)
493 struct connected
*ifc
;
495 ifc
= zebra_interface_address_read(cmd
, zclient
->ibuf
, vrf_id
);
499 debugf(NHRP_DEBUG_IF
, "if-addr-del: %s: %pFX", ifc
->ifp
->name
,
502 nhrp_interface_update_address(
503 ifc
->ifp
, family2afi(PREFIX_FAMILY(ifc
->address
)), 0);
504 connected_free(&ifc
);
509 void nhrp_interface_notify_add(struct interface
*ifp
, struct notifier_block
*n
,
512 struct nhrp_interface
*nifp
= ifp
->info
;
514 notifier_add(n
, &nifp
->notifier_list
, fn
);
517 void nhrp_interface_notify_del(struct interface
*ifp
, struct notifier_block
*n
)
519 struct nhrp_interface
*nifp
= ifp
->info
;
521 notifier_del(n
, &nifp
->notifier_list
);
524 void nhrp_interface_set_protection(struct interface
*ifp
, const char *profile
,
525 const char *fallback_profile
)
527 struct nhrp_interface
*nifp
= ifp
->info
;
529 if (nifp
->ipsec_profile
) {
530 vici_terminate_vc_by_profile_name(nifp
->ipsec_profile
);
532 free(nifp
->ipsec_profile
);
534 nifp
->ipsec_profile
= profile
? strdup(profile
) : NULL
;
536 if (nifp
->ipsec_fallback_profile
) {
537 vici_terminate_vc_by_profile_name(nifp
->ipsec_fallback_profile
);
539 free(nifp
->ipsec_fallback_profile
);
541 nifp
->ipsec_fallback_profile
=
542 fallback_profile
? strdup(fallback_profile
) : NULL
;
544 notifier_call(&nifp
->notifier_list
, NOTIFY_INTERFACE_IPSEC_CHANGED
);
547 void nhrp_interface_set_source(struct interface
*ifp
, const char *ifname
)
549 struct nhrp_interface
*nifp
= ifp
->info
;
553 nifp
->source
= ifname
? strdup(ifname
) : NULL
;
555 nhrp_interface_update_nbma(ifp
, NULL
);