]> git.proxmox.com Git - mirror_frr.git/blame - nhrpd/nhrp_interface.c
nhrpd: add a zebra api to configure neighbor table per interface
[mirror_frr.git] / nhrpd / nhrp_interface.c
CommitLineData
2fb975da
TT
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
b45ac5f5
DL
10#ifdef HAVE_CONFIG_H
11#include "config.h"
12#endif
13
2fb975da
TT
14#include <net/if_arp.h>
15#include "zebra.h"
16#include "linklist.h"
17#include "memory.h"
18#include "thread.h"
19
20#include "nhrpd.h"
21#include "os.h"
22#include "netlink.h"
23
bf8d3d6a 24DEFINE_MTYPE_STATIC(NHRPD, NHRP_IF, "NHRP interface");
819dc8bb 25
fef2ed13
PG
26static void nhrp_interface_update_cache_config(struct interface *ifp,
27 bool available,
28 uint8_t family);
29
2fb975da
TT
30static int nhrp_if_new_hook(struct interface *ifp)
31{
32 struct nhrp_interface *nifp;
33 afi_t afi;
34
35 nifp = XCALLOC(MTYPE_NHRP_IF, sizeof(struct nhrp_interface));
2fb975da
TT
36
37 ifp->info = nifp;
38 nifp->ifp = ifp;
39
40 notifier_init(&nifp->notifier_list);
41 for (afi = 0; afi < AFI_MAX; afi++) {
42 struct nhrp_afi_data *ad = &nifp->afi[afi];
43 ad->holdtime = NHRPD_DEFAULT_HOLDTIME;
44 list_init(&ad->nhslist_head);
45 }
46
47 return 0;
48}
49
50static int nhrp_if_delete_hook(struct interface *ifp)
51{
ee72f0a0
RD
52 struct nhrp_interface *nifp = ifp->info;
53
54 debugf(NHRP_DEBUG_IF, "Deleted interface (%s)", ifp->name);
55
56 nhrp_cache_interface_del(ifp);
57 nhrp_nhs_interface_del(ifp);
58 nhrp_peer_interface_del(ifp);
59
60 if (nifp->ipsec_profile)
61 free(nifp->ipsec_profile);
62 if (nifp->ipsec_fallback_profile)
63 free(nifp->ipsec_fallback_profile);
64 if (nifp->source)
65 free(nifp->source);
66
2fb975da
TT
67 XFREE(MTYPE_NHRP_IF, ifp->info);
68 return 0;
69}
70
71void nhrp_interface_init(void)
72{
ce19a04a
DL
73 hook_register_prio(if_add, 0, nhrp_if_new_hook);
74 hook_register_prio(if_del, 0, nhrp_if_delete_hook);
2fb975da
TT
75}
76
77void nhrp_interface_update_mtu(struct interface *ifp, afi_t afi)
78{
79 struct nhrp_interface *nifp = ifp->info;
80 struct nhrp_afi_data *if_ad = &nifp->afi[afi];
81 unsigned short new_mtu;
82
83 if (if_ad->configured_mtu < 0)
84 new_mtu = nifp->nbmaifp ? nifp->nbmaifp->mtu : 0;
85 else
86 new_mtu = if_ad->configured_mtu;
87 if (new_mtu >= 1500)
88 new_mtu = 0;
89
90 if (new_mtu != if_ad->mtu) {
996c9314
LB
91 debugf(NHRP_DEBUG_IF, "%s: MTU changed to %d", ifp->name,
92 new_mtu);
2fb975da 93 if_ad->mtu = new_mtu;
996c9314
LB
94 notifier_call(&nifp->notifier_list,
95 NOTIFY_INTERFACE_MTU_CHANGED);
2fb975da
TT
96 }
97}
98
99static void nhrp_interface_update_source(struct interface *ifp)
100{
101 struct nhrp_interface *nifp = ifp->info;
102
996c9314
LB
103 if (!nifp->source || !nifp->nbmaifp
104 || (ifindex_t)nifp->linkidx == nifp->nbmaifp->ifindex)
2fb975da
TT
105 return;
106
107 nifp->linkidx = nifp->nbmaifp->ifindex;
996c9314
LB
108 debugf(NHRP_DEBUG_IF, "%s: bound device index changed to %d", ifp->name,
109 nifp->linkidx);
2fb975da
TT
110 netlink_gre_set_link(ifp->ifindex, nifp->linkidx);
111}
112
996c9314
LB
113static void nhrp_interface_interface_notifier(struct notifier_block *n,
114 unsigned long cmd)
2fb975da 115{
996c9314
LB
116 struct nhrp_interface *nifp =
117 container_of(n, struct nhrp_interface, nbmanifp_notifier);
2fb975da
TT
118 struct interface *nbmaifp = nifp->nbmaifp;
119 struct nhrp_interface *nbmanifp = nbmaifp->info;
2fb975da
TT
120
121 switch (cmd) {
122 case NOTIFY_INTERFACE_CHANGED:
123 nhrp_interface_update_mtu(nifp->ifp, AFI_IP);
124 nhrp_interface_update_source(nifp->ifp);
125 break;
126 case NOTIFY_INTERFACE_ADDRESS_CHANGED:
127 nifp->nbma = nbmanifp->afi[AFI_IP].addr;
128 nhrp_interface_update(nifp->ifp);
996c9314
LB
129 notifier_call(&nifp->notifier_list,
130 NOTIFY_INTERFACE_NBMA_CHANGED);
b6c48481
DS
131 debugf(NHRP_DEBUG_IF, "%s: NBMA change: address %pSU",
132 nifp->ifp->name, &nifp->nbma);
2fb975da
TT
133 break;
134 }
135}
136
137static void nhrp_interface_update_nbma(struct interface *ifp)
138{
139 struct nhrp_interface *nifp = ifp->info, *nbmanifp = NULL;
140 struct interface *nbmaifp = NULL;
141 union sockunion nbma;
142
143 sockunion_family(&nbma) = AF_UNSPEC;
144
145 if (nifp->source)
a36898e7 146 nbmaifp = if_lookup_by_name(nifp->source, VRF_DEFAULT);
2fb975da
TT
147
148 switch (ifp->ll_type) {
149 case ZEBRA_LLT_IPGRE: {
996c9314
LB
150 struct in_addr saddr = {0};
151 netlink_gre_get_info(ifp->ifindex, &nifp->grekey,
152 &nifp->linkidx, &saddr);
153 debugf(NHRP_DEBUG_IF, "%s: GRE: %x %x %x", ifp->name,
154 nifp->grekey, nifp->linkidx, saddr.s_addr);
975a328e 155 if (saddr.s_addr != INADDR_ANY)
d7c0a89a 156 sockunion_set(&nbma, AF_INET, (uint8_t *)&saddr.s_addr,
996c9314
LB
157 sizeof(saddr.s_addr));
158 else if (!nbmaifp && nifp->linkidx != IFINDEX_INTERNAL)
159 nbmaifp =
160 if_lookup_by_index(nifp->linkidx, VRF_DEFAULT);
161 } break;
2fb975da
TT
162 default:
163 break;
164 }
165
166 if (nbmaifp)
167 nbmanifp = nbmaifp->info;
168
169 if (nbmaifp != nifp->nbmaifp) {
170 if (nifp->nbmaifp)
171 notifier_del(&nifp->nbmanifp_notifier);
172 nifp->nbmaifp = nbmaifp;
173 if (nbmaifp) {
996c9314
LB
174 notifier_add(&nifp->nbmanifp_notifier,
175 &nbmanifp->notifier_list,
176 nhrp_interface_interface_notifier);
177 debugf(NHRP_DEBUG_IF, "%s: bound to %s", ifp->name,
178 nbmaifp->name);
2fb975da
TT
179 }
180 }
181
182 if (nbmaifp) {
183 if (sockunion_family(&nbma) == AF_UNSPEC)
184 nbma = nbmanifp->afi[AFI_IP].addr;
185 nhrp_interface_update_mtu(ifp, AFI_IP);
186 nhrp_interface_update_source(ifp);
187 }
188
189 if (!sockunion_same(&nbma, &nifp->nbma)) {
190 nifp->nbma = nbma;
191 nhrp_interface_update(nifp->ifp);
192 debugf(NHRP_DEBUG_IF, "%s: NBMA address changed", ifp->name);
996c9314
LB
193 notifier_call(&nifp->notifier_list,
194 NOTIFY_INTERFACE_NBMA_CHANGED);
2fb975da
TT
195 }
196
197 nhrp_interface_update(ifp);
198}
199
996c9314
LB
200static void nhrp_interface_update_address(struct interface *ifp, afi_t afi,
201 int force)
2fb975da
TT
202{
203 const int family = afi2family(afi);
204 struct nhrp_interface *nifp = ifp->info;
205 struct nhrp_afi_data *if_ad = &nifp->afi[afi];
206 struct nhrp_cache *nc;
207 struct connected *c, *best;
208 struct listnode *cnode;
209 union sockunion addr;
210 char buf[PREFIX_STRLEN];
211
212 /* Select new best match preferring primary address */
213 best = NULL;
214 for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, c)) {
215 if (PREFIX_FAMILY(c->address) != family)
216 continue;
217 if (best == NULL) {
218 best = c;
219 continue;
220 }
996c9314
LB
221 if ((best->flags & ZEBRA_IFA_SECONDARY)
222 && !(c->flags & ZEBRA_IFA_SECONDARY)) {
2fb975da
TT
223 best = c;
224 continue;
225 }
996c9314
LB
226 if (!(best->flags & ZEBRA_IFA_SECONDARY)
227 && (c->flags & ZEBRA_IFA_SECONDARY))
2fb975da
TT
228 continue;
229 if (best->address->prefixlen > c->address->prefixlen) {
230 best = c;
231 continue;
232 }
233 if (best->address->prefixlen < c->address->prefixlen)
234 continue;
235 }
236
237 /* On NHRP interfaces a host prefix is required */
996c9314
LB
238 if (best && if_ad->configured
239 && best->address->prefixlen != 8 * prefix_blen(best->address)) {
2dbe669b
DA
240 zlog_notice("%s: %pFX is not a host prefix", ifp->name,
241 best->address);
2fb975da
TT
242 best = NULL;
243 }
244
245 /* Update address if it changed */
246 if (best)
247 prefix2sockunion(best->address, &addr);
248 else
249 memset(&addr, 0, sizeof(addr));
250
251 if (!force && sockunion_same(&if_ad->addr, &addr))
252 return;
253
254 if (sockunion_family(&if_ad->addr) != AF_UNSPEC) {
255 nc = nhrp_cache_get(ifp, &if_ad->addr, 0);
996c9314
LB
256 if (nc)
257 nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, -1,
85365e51 258 NULL, 0, NULL, NULL);
2fb975da
TT
259 }
260
996c9314
LB
261 debugf(NHRP_DEBUG_KERNEL, "%s: IPv%d address changed to %s", ifp->name,
262 afi == AFI_IP ? 4 : 6,
0d6f7fd6 263 best ? prefix2str(best->address, buf, sizeof(buf)) : "(none)");
2fb975da
TT
264 if_ad->addr = addr;
265
266 if (if_ad->configured && sockunion_family(&if_ad->addr) != AF_UNSPEC) {
267 nc = nhrp_cache_get(ifp, &addr, 1);
996c9314
LB
268 if (nc)
269 nhrp_cache_update_binding(nc, NHRP_CACHE_LOCAL, 0, NULL,
85365e51 270 0, NULL, NULL);
2fb975da
TT
271 }
272
273 notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_ADDRESS_CHANGED);
274}
275
276void nhrp_interface_update(struct interface *ifp)
277{
278 struct nhrp_interface *nifp = ifp->info;
279 struct nhrp_afi_data *if_ad;
280 afi_t afi;
281 int enabled = 0;
282
283 notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_CHANGED);
284
285 for (afi = 0; afi < AFI_MAX; afi++) {
286 if_ad = &nifp->afi[afi];
287
996c9314
LB
288 if (sockunion_family(&nifp->nbma) == AF_UNSPEC
289 || ifp->ifindex == IFINDEX_INTERNAL || !if_is_up(ifp)
290 || !if_ad->network_id) {
2fb975da
TT
291 if (if_ad->configured) {
292 if_ad->configured = 0;
293 nhrp_interface_update_address(ifp, afi, 1);
294 }
295 continue;
296 }
297
298 if (!if_ad->configured) {
996c9314
LB
299 os_configure_dmvpn(ifp->ifindex, ifp->name,
300 afi2family(afi));
f468a45a 301 nhrp_send_zebra_configure_arp(ifp, afi2family(afi));
2fb975da
TT
302 if_ad->configured = 1;
303 nhrp_interface_update_address(ifp, afi, 1);
304 }
305
306 enabled = 1;
307 }
308
309 if (enabled != nifp->enabled) {
310 nifp->enabled = enabled;
996c9314
LB
311 notifier_call(&nifp->notifier_list,
312 enabled ? NOTIFY_INTERFACE_UP
313 : NOTIFY_INTERFACE_DOWN);
2fb975da
TT
314 }
315}
316
ef7bd2a3 317int nhrp_ifp_create(struct interface *ifp)
2fb975da 318{
2fb975da 319 debugf(NHRP_DEBUG_IF, "if-add: %s, ifindex: %u, hw_type: %d %s",
996c9314
LB
320 ifp->name, ifp->ifindex, ifp->ll_type,
321 if_link_type_str(ifp->ll_type));
2fb975da
TT
322
323 nhrp_interface_update_nbma(ifp);
324
325 return 0;
326}
327
3c3c3252 328int nhrp_ifp_destroy(struct interface *ifp)
2fb975da 329{
2fb975da 330 debugf(NHRP_DEBUG_IF, "if-delete: %s", ifp->name);
9d6c33ea 331
fef2ed13
PG
332 nhrp_interface_update_cache_config(ifp, false, AF_INET);
333 nhrp_interface_update_cache_config(ifp, false, AF_INET6);
2fb975da 334 nhrp_interface_update(ifp);
9d6c33ea 335
2fb975da
TT
336 return 0;
337}
338
fef2ed13
PG
339struct map_ctx {
340 int family;
341 bool enabled;
342};
343
e5773617
MS
344static void interface_config_update_nhrp_map(struct nhrp_cache_config *cc,
345 void *data)
fef2ed13
PG
346{
347 struct map_ctx *ctx = data;
348 struct interface *ifp = cc->ifp;
349 struct nhrp_cache *c;
350 union sockunion nbma_addr;
351
352 if (sockunion_family(&cc->remote_addr) != ctx->family)
353 return;
354
355 /* gre layer not ready */
356 if (ifp->vrf_id == VRF_UNKNOWN)
357 return;
358
359 c = nhrp_cache_get(ifp, &cc->remote_addr, ctx->enabled ? 1 : 0);
360 if (!c && !ctx->enabled)
361 return;
e5773617 362
fef2ed13
PG
363 /* suppress */
364 if (!ctx->enabled) {
365 if (c && c->map) {
e5773617
MS
366 nhrp_cache_update_binding(
367 c, c->cur.type, -1,
85365e51 368 nhrp_peer_get(ifp, &nbma_addr), 0, NULL, NULL);
fef2ed13
PG
369 }
370 return;
371 }
e5773617
MS
372
373 /* Newly created */
374 assert(c != NULL);
375
fef2ed13
PG
376 c->map = 1;
377 if (cc->type == NHRP_CACHE_LOCAL)
378 nhrp_cache_update_binding(c, NHRP_CACHE_LOCAL, 0, NULL, 0,
85365e51 379 NULL, NULL);
fef2ed13
PG
380 else {
381 nhrp_cache_update_binding(c, NHRP_CACHE_STATIC, 0,
382 nhrp_peer_get(ifp, &cc->nbma), 0,
85365e51 383 NULL, NULL);
fef2ed13
PG
384 }
385}
386
387static void nhrp_interface_update_cache_config(struct interface *ifp, bool available, uint8_t family)
388{
389 struct map_ctx mapctx;
390
391 mapctx = (struct map_ctx){
392 .family = family,
393 .enabled = available
394 };
395 nhrp_cache_config_foreach(ifp, interface_config_update_nhrp_map,
396 &mapctx);
397
398}
399
ddbf3e60 400int nhrp_ifp_up(struct interface *ifp)
2fb975da 401{
2fb975da
TT
402 debugf(NHRP_DEBUG_IF, "if-up: %s", ifp->name);
403 nhrp_interface_update_nbma(ifp);
404
405 return 0;
406}
407
b0b69e59 408int nhrp_ifp_down(struct interface *ifp)
2fb975da 409{
2fb975da
TT
410 debugf(NHRP_DEBUG_IF, "if-down: %s", ifp->name);
411 nhrp_interface_update(ifp);
b0b69e59 412
2fb975da
TT
413 return 0;
414}
415
121f9dee 416int nhrp_interface_address_add(ZAPI_CALLBACK_ARGS)
2fb975da
TT
417{
418 struct connected *ifc;
2fb975da 419
121f9dee 420 ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
2fb975da
TT
421 if (ifc == NULL)
422 return 0;
423
2dbe669b
DA
424 debugf(NHRP_DEBUG_IF, "if-addr-add: %s: %pFX", ifc->ifp->name,
425 ifc->address);
2fb975da 426
996c9314
LB
427 nhrp_interface_update_address(
428 ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
fef2ed13 429 nhrp_interface_update_cache_config(ifc->ifp, true, PREFIX_FAMILY(ifc->address));
2fb975da
TT
430 return 0;
431}
432
121f9dee 433int nhrp_interface_address_delete(ZAPI_CALLBACK_ARGS)
2fb975da
TT
434{
435 struct connected *ifc;
2fb975da 436
121f9dee 437 ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
2fb975da
TT
438 if (ifc == NULL)
439 return 0;
440
2dbe669b
DA
441 debugf(NHRP_DEBUG_IF, "if-addr-del: %s: %pFX", ifc->ifp->name,
442 ifc->address);
2fb975da 443
996c9314
LB
444 nhrp_interface_update_address(
445 ifc->ifp, family2afi(PREFIX_FAMILY(ifc->address)), 0);
721c0857 446 connected_free(&ifc);
2fb975da
TT
447
448 return 0;
449}
450
996c9314
LB
451void nhrp_interface_notify_add(struct interface *ifp, struct notifier_block *n,
452 notifier_fn_t fn)
2fb975da
TT
453{
454 struct nhrp_interface *nifp = ifp->info;
455 notifier_add(n, &nifp->notifier_list, fn);
456}
457
458void nhrp_interface_notify_del(struct interface *ifp, struct notifier_block *n)
459{
460 notifier_del(n);
461}
462
996c9314
LB
463void nhrp_interface_set_protection(struct interface *ifp, const char *profile,
464 const char *fallback_profile)
2fb975da
TT
465{
466 struct nhrp_interface *nifp = ifp->info;
467
74e5ba3a 468 if (nifp->ipsec_profile) {
083bbfae 469 vici_terminate_vc_by_profile_name(nifp->ipsec_profile);
58ef1668 470 nhrp_vc_reset();
996c9314 471 free(nifp->ipsec_profile);
083bbfae 472 }
2fb975da
TT
473 nifp->ipsec_profile = profile ? strdup(profile) : NULL;
474
74e5ba3a 475 if (nifp->ipsec_fallback_profile) {
083bbfae 476 vici_terminate_vc_by_profile_name(nifp->ipsec_fallback_profile);
58ef1668 477 nhrp_vc_reset();
996c9314 478 free(nifp->ipsec_fallback_profile);
083bbfae 479 }
996c9314
LB
480 nifp->ipsec_fallback_profile =
481 fallback_profile ? strdup(fallback_profile) : NULL;
111aec1e 482
58ef1668 483 notifier_call(&nifp->notifier_list, NOTIFY_INTERFACE_IPSEC_CHANGED);
2fb975da
TT
484}
485
486void nhrp_interface_set_source(struct interface *ifp, const char *ifname)
487{
488 struct nhrp_interface *nifp = ifp->info;
489
996c9314
LB
490 if (nifp->source)
491 free(nifp->source);
2fb975da
TT
492 nifp->source = ifname ? strdup(ifname) : NULL;
493
494 nhrp_interface_update_nbma(ifp);
495}