1 |
|
|
/* $OpenBSD: rde.c,v 1.347 2016/07/21 10:13:58 claudio Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/types.h> |
20 |
|
|
#include <sys/socket.h> |
21 |
|
|
#include <sys/time.h> |
22 |
|
|
#include <sys/resource.h> |
23 |
|
|
|
24 |
|
|
#include <errno.h> |
25 |
|
|
#include <ifaddrs.h> |
26 |
|
|
#include <pwd.h> |
27 |
|
|
#include <poll.h> |
28 |
|
|
#include <signal.h> |
29 |
|
|
#include <stdio.h> |
30 |
|
|
#include <stdlib.h> |
31 |
|
|
#include <string.h> |
32 |
|
|
#include <unistd.h> |
33 |
|
|
#include <err.h> |
34 |
|
|
|
35 |
|
|
#include "bgpd.h" |
36 |
|
|
#include "mrt.h" |
37 |
|
|
#include "rde.h" |
38 |
|
|
#include "session.h" |
39 |
|
|
|
40 |
|
|
#define PFD_PIPE_MAIN 0 |
41 |
|
|
#define PFD_PIPE_SESSION 1 |
42 |
|
|
#define PFD_PIPE_SESSION_CTL 2 |
43 |
|
|
#define PFD_PIPE_COUNT 3 |
44 |
|
|
|
45 |
|
|
void rde_sighdlr(int); |
46 |
|
|
void rde_dispatch_imsg_session(struct imsgbuf *); |
47 |
|
|
void rde_dispatch_imsg_parent(struct imsgbuf *); |
48 |
|
|
int rde_update_dispatch(struct imsg *); |
49 |
|
|
void rde_update_update(struct rde_peer *, struct rde_aspath *, |
50 |
|
|
struct bgpd_addr *, u_int8_t); |
51 |
|
|
void rde_update_withdraw(struct rde_peer *, struct bgpd_addr *, |
52 |
|
|
u_int8_t); |
53 |
|
|
int rde_attr_parse(u_char *, u_int16_t, struct rde_peer *, |
54 |
|
|
struct rde_aspath *, struct mpattr *); |
55 |
|
|
int rde_attr_add(struct rde_aspath *, u_char *, u_int16_t); |
56 |
|
|
u_int8_t rde_attr_missing(struct rde_aspath *, int, u_int16_t); |
57 |
|
|
int rde_get_mp_nexthop(u_char *, u_int16_t, u_int8_t, |
58 |
|
|
struct rde_aspath *); |
59 |
|
|
int rde_update_extract_prefix(u_char *, u_int16_t, void *, |
60 |
|
|
u_int8_t, u_int8_t); |
61 |
|
|
int rde_update_get_prefix(u_char *, u_int16_t, struct bgpd_addr *, |
62 |
|
|
u_int8_t *); |
63 |
|
|
int rde_update_get_prefix6(u_char *, u_int16_t, struct bgpd_addr *, |
64 |
|
|
u_int8_t *); |
65 |
|
|
int rde_update_get_vpn4(u_char *, u_int16_t, struct bgpd_addr *, |
66 |
|
|
u_int8_t *); |
67 |
|
|
void rde_update_err(struct rde_peer *, u_int8_t , u_int8_t, |
68 |
|
|
void *, u_int16_t); |
69 |
|
|
void rde_update_log(const char *, u_int16_t, |
70 |
|
|
const struct rde_peer *, const struct bgpd_addr *, |
71 |
|
|
const struct bgpd_addr *, u_int8_t); |
72 |
|
|
void rde_as4byte_fixup(struct rde_peer *, struct rde_aspath *); |
73 |
|
|
void rde_reflector(struct rde_peer *, struct rde_aspath *); |
74 |
|
|
|
75 |
|
|
void rde_dump_rib_as(struct prefix *, struct rde_aspath *,pid_t, |
76 |
|
|
int); |
77 |
|
|
void rde_dump_filter(struct prefix *, |
78 |
|
|
struct ctl_show_rib_request *); |
79 |
|
|
void rde_dump_filterout(struct rde_peer *, struct prefix *, |
80 |
|
|
struct ctl_show_rib_request *); |
81 |
|
|
void rde_dump_upcall(struct rib_entry *, void *); |
82 |
|
|
void rde_dump_prefix_upcall(struct rib_entry *, void *); |
83 |
|
|
void rde_dump_ctx_new(struct ctl_show_rib_request *, pid_t, |
84 |
|
|
enum imsg_type); |
85 |
|
|
void rde_dump_mrt_new(struct mrt *, pid_t, int); |
86 |
|
|
void rde_dump_done(void *); |
87 |
|
|
|
88 |
|
|
int rde_rdomain_import(struct rde_aspath *, struct rdomain *); |
89 |
|
|
void rde_reload_done(void); |
90 |
|
|
void rde_softreconfig_out(struct rib_entry *, void *); |
91 |
|
|
void rde_softreconfig_in(struct rib_entry *, void *); |
92 |
|
|
void rde_softreconfig_unload_peer(struct rib_entry *, void *); |
93 |
|
|
void rde_up_dump_upcall(struct rib_entry *, void *); |
94 |
|
|
void rde_update_queue_runner(void); |
95 |
|
|
void rde_update6_queue_runner(u_int8_t); |
96 |
|
|
|
97 |
|
|
void peer_init(u_int32_t); |
98 |
|
|
void peer_shutdown(void); |
99 |
|
|
int peer_localaddrs(struct rde_peer *, struct bgpd_addr *); |
100 |
|
|
struct rde_peer *peer_add(u_int32_t, struct peer_config *); |
101 |
|
|
struct rde_peer *peer_get(u_int32_t); |
102 |
|
|
void peer_up(u_int32_t, struct session_up *); |
103 |
|
|
void peer_down(u_int32_t); |
104 |
|
|
void peer_flush(struct rde_peer *, u_int8_t); |
105 |
|
|
void peer_stale(u_int32_t, u_int8_t); |
106 |
|
|
void peer_recv_eor(struct rde_peer *, u_int8_t); |
107 |
|
|
void peer_dump(u_int32_t, u_int8_t); |
108 |
|
|
void peer_send_eor(struct rde_peer *, u_int8_t); |
109 |
|
|
|
110 |
|
|
void network_add(struct network_config *, int); |
111 |
|
|
void network_delete(struct network_config *, int); |
112 |
|
|
void network_dump_upcall(struct rib_entry *, void *); |
113 |
|
|
|
114 |
|
|
void rde_shutdown(void); |
115 |
|
|
int sa_cmp(struct bgpd_addr *, struct sockaddr *); |
116 |
|
|
|
117 |
|
|
volatile sig_atomic_t rde_quit = 0; |
118 |
|
|
struct bgpd_config *conf, *nconf; |
119 |
|
|
time_t reloadtime; |
120 |
|
|
struct rde_peer_head peerlist; |
121 |
|
|
struct rde_peer *peerself; |
122 |
|
|
struct filter_head *out_rules, *out_rules_tmp; |
123 |
|
|
struct rdomain_head *rdomains_l, *newdomains; |
124 |
|
|
struct imsgbuf *ibuf_se; |
125 |
|
|
struct imsgbuf *ibuf_se_ctl; |
126 |
|
|
struct imsgbuf *ibuf_main; |
127 |
|
|
struct rde_memstats rdemem; |
128 |
|
|
|
129 |
|
|
struct rde_dump_ctx { |
130 |
|
|
struct rib_context ribctx; |
131 |
|
|
struct ctl_show_rib_request req; |
132 |
|
|
sa_family_t af; |
133 |
|
|
}; |
134 |
|
|
|
135 |
|
|
struct rde_mrt_ctx { |
136 |
|
|
struct mrt mrt; |
137 |
|
|
struct rib_context ribctx; |
138 |
|
|
LIST_ENTRY(rde_mrt_ctx) entry; |
139 |
|
|
}; |
140 |
|
|
|
141 |
|
|
LIST_HEAD(, rde_mrt_ctx) rde_mrts = LIST_HEAD_INITIALIZER(rde_mrts); |
142 |
|
|
u_int rde_mrt_cnt; |
143 |
|
|
|
144 |
|
|
void |
145 |
|
|
rde_sighdlr(int sig) |
146 |
|
|
{ |
147 |
|
|
switch (sig) { |
148 |
|
|
case SIGINT: |
149 |
|
|
case SIGTERM: |
150 |
|
|
rde_quit = 1; |
151 |
|
|
break; |
152 |
|
|
} |
153 |
|
|
} |
154 |
|
|
|
155 |
|
|
u_int32_t peerhashsize = 64; |
156 |
|
|
u_int32_t pathhashsize = 1024; |
157 |
|
|
u_int32_t attrhashsize = 512; |
158 |
|
|
u_int32_t nexthophashsize = 64; |
159 |
|
|
|
160 |
|
|
void |
161 |
|
|
rde_main(int debug, int verbose) |
162 |
|
|
{ |
163 |
|
|
struct passwd *pw; |
164 |
|
|
struct pollfd *pfd = NULL; |
165 |
|
|
struct rde_mrt_ctx *mctx, *xmctx; |
166 |
|
|
void *newp; |
167 |
|
|
u_int pfd_elms = 0, i, j; |
168 |
|
|
int timeout; |
169 |
|
|
u_int8_t aid; |
170 |
|
|
|
171 |
|
|
log_init(debug); |
172 |
|
|
log_verbose(verbose); |
173 |
|
|
|
174 |
|
|
if ((pw = getpwnam(BGPD_USER)) == NULL) |
175 |
|
|
fatal("getpwnam"); |
176 |
|
|
|
177 |
|
|
if (chroot(pw->pw_dir) == -1) |
178 |
|
|
fatal("chroot"); |
179 |
|
|
if (chdir("/") == -1) |
180 |
|
|
fatal("chdir(\"/\")"); |
181 |
|
|
|
182 |
|
|
setproctitle("route decision engine"); |
183 |
|
|
bgpd_process = PROC_RDE; |
184 |
|
|
|
185 |
|
|
if (setgroups(1, &pw->pw_gid) || |
186 |
|
|
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || |
187 |
|
|
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) |
188 |
|
|
fatal("can't drop privileges"); |
189 |
|
|
|
190 |
|
|
if (pledge("stdio route recvfd rpath wpath cpath", NULL) == -1) |
191 |
|
|
fatal("pledge"); |
192 |
|
|
|
193 |
|
|
signal(SIGTERM, rde_sighdlr); |
194 |
|
|
signal(SIGINT, rde_sighdlr); |
195 |
|
|
signal(SIGPIPE, SIG_IGN); |
196 |
|
|
signal(SIGHUP, SIG_IGN); |
197 |
|
|
signal(SIGALRM, SIG_IGN); |
198 |
|
|
signal(SIGUSR1, SIG_IGN); |
199 |
|
|
|
200 |
|
|
/* initialize the RIB structures */ |
201 |
|
|
if ((ibuf_main = malloc(sizeof(struct imsgbuf))) == NULL) |
202 |
|
|
fatal(NULL); |
203 |
|
|
imsg_init(ibuf_main, 3); |
204 |
|
|
|
205 |
|
|
pt_init(); |
206 |
|
|
path_init(pathhashsize); |
207 |
|
|
aspath_init(pathhashsize); |
208 |
|
|
attr_init(attrhashsize); |
209 |
|
|
nexthop_init(nexthophashsize); |
210 |
|
|
peer_init(peerhashsize); |
211 |
|
|
|
212 |
|
|
out_rules = calloc(1, sizeof(struct filter_head)); |
213 |
|
|
if (out_rules == NULL) |
214 |
|
|
fatal(NULL); |
215 |
|
|
TAILQ_INIT(out_rules); |
216 |
|
|
rdomains_l = calloc(1, sizeof(struct rdomain_head)); |
217 |
|
|
if (rdomains_l == NULL) |
218 |
|
|
fatal(NULL); |
219 |
|
|
SIMPLEQ_INIT(rdomains_l); |
220 |
|
|
if ((conf = calloc(1, sizeof(struct bgpd_config))) == NULL) |
221 |
|
|
fatal(NULL); |
222 |
|
|
log_info("route decision engine ready"); |
223 |
|
|
|
224 |
|
|
while (rde_quit == 0) { |
225 |
|
|
if (pfd_elms < PFD_PIPE_COUNT + rde_mrt_cnt) { |
226 |
|
|
if ((newp = reallocarray(pfd, |
227 |
|
|
PFD_PIPE_COUNT + rde_mrt_cnt, |
228 |
|
|
sizeof(struct pollfd))) == NULL) { |
229 |
|
|
/* panic for now */ |
230 |
|
|
log_warn("could not resize pfd from %u -> %u" |
231 |
|
|
" entries", pfd_elms, PFD_PIPE_COUNT + |
232 |
|
|
rde_mrt_cnt); |
233 |
|
|
fatalx("exiting"); |
234 |
|
|
} |
235 |
|
|
pfd = newp; |
236 |
|
|
pfd_elms = PFD_PIPE_COUNT + rde_mrt_cnt; |
237 |
|
|
} |
238 |
|
|
timeout = INFTIM; |
239 |
|
|
bzero(pfd, sizeof(struct pollfd) * pfd_elms); |
240 |
|
|
|
241 |
|
|
set_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main); |
242 |
|
|
set_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se); |
243 |
|
|
set_pollfd(&pfd[PFD_PIPE_SESSION_CTL], ibuf_se_ctl); |
244 |
|
|
|
245 |
|
|
if (rib_dump_pending() && |
246 |
|
|
ibuf_se_ctl && ibuf_se_ctl->w.queued == 0) |
247 |
|
|
timeout = 0; |
248 |
|
|
|
249 |
|
|
i = PFD_PIPE_COUNT; |
250 |
|
|
for (mctx = LIST_FIRST(&rde_mrts); mctx != 0; mctx = xmctx) { |
251 |
|
|
xmctx = LIST_NEXT(mctx, entry); |
252 |
|
|
if (mctx->mrt.wbuf.queued) { |
253 |
|
|
pfd[i].fd = mctx->mrt.wbuf.fd; |
254 |
|
|
pfd[i].events = POLLOUT; |
255 |
|
|
i++; |
256 |
|
|
} else if (mctx->mrt.state == MRT_STATE_REMOVE) { |
257 |
|
|
close(mctx->mrt.wbuf.fd); |
258 |
|
|
LIST_REMOVE(&mctx->ribctx, entry); |
259 |
|
|
LIST_REMOVE(mctx, entry); |
260 |
|
|
free(mctx); |
261 |
|
|
rde_mrt_cnt--; |
262 |
|
|
} |
263 |
|
|
} |
264 |
|
|
|
265 |
|
|
if (poll(pfd, i, timeout) == -1) { |
266 |
|
|
if (errno != EINTR) |
267 |
|
|
fatal("poll error"); |
268 |
|
|
continue; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
if (handle_pollfd(&pfd[PFD_PIPE_MAIN], ibuf_main) == -1) |
272 |
|
|
fatalx("Lost connection to parent"); |
273 |
|
|
else |
274 |
|
|
rde_dispatch_imsg_parent(ibuf_main); |
275 |
|
|
|
276 |
|
|
if (handle_pollfd(&pfd[PFD_PIPE_SESSION], ibuf_se) == -1) { |
277 |
|
|
log_warnx("RDE: Lost connection to SE"); |
278 |
|
|
msgbuf_clear(&ibuf_se->w); |
279 |
|
|
free(ibuf_se); |
280 |
|
|
ibuf_se = NULL; |
281 |
|
|
} else |
282 |
|
|
rde_dispatch_imsg_session(ibuf_se); |
283 |
|
|
|
284 |
|
|
if (handle_pollfd(&pfd[PFD_PIPE_SESSION_CTL], ibuf_se_ctl) == |
285 |
|
|
-1) { |
286 |
|
|
log_warnx("RDE: Lost connection to SE control"); |
287 |
|
|
msgbuf_clear(&ibuf_se_ctl->w); |
288 |
|
|
free(ibuf_se_ctl); |
289 |
|
|
ibuf_se_ctl = NULL; |
290 |
|
|
} else |
291 |
|
|
rde_dispatch_imsg_session(ibuf_se_ctl); |
292 |
|
|
|
293 |
|
|
for (j = PFD_PIPE_COUNT, mctx = LIST_FIRST(&rde_mrts); |
294 |
|
|
j < i && mctx != 0; j++) { |
295 |
|
|
if (pfd[j].fd == mctx->mrt.wbuf.fd && |
296 |
|
|
pfd[j].revents & POLLOUT) |
297 |
|
|
mrt_write(&mctx->mrt); |
298 |
|
|
mctx = LIST_NEXT(mctx, entry); |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
rde_update_queue_runner(); |
302 |
|
|
for (aid = AID_INET6; aid < AID_MAX; aid++) |
303 |
|
|
rde_update6_queue_runner(aid); |
304 |
|
|
if (rib_dump_pending() && |
305 |
|
|
ibuf_se_ctl && ibuf_se_ctl->w.queued <= 10) |
306 |
|
|
rib_dump_runner(); |
307 |
|
|
} |
308 |
|
|
|
309 |
|
|
/* do not clean up on shutdown on production, it takes ages. */ |
310 |
|
|
if (debug) |
311 |
|
|
rde_shutdown(); |
312 |
|
|
|
313 |
|
|
while ((mctx = LIST_FIRST(&rde_mrts)) != NULL) { |
314 |
|
|
msgbuf_clear(&mctx->mrt.wbuf); |
315 |
|
|
close(mctx->mrt.wbuf.fd); |
316 |
|
|
LIST_REMOVE(&mctx->ribctx, entry); |
317 |
|
|
LIST_REMOVE(mctx, entry); |
318 |
|
|
free(mctx); |
319 |
|
|
} |
320 |
|
|
|
321 |
|
|
if (ibuf_se) |
322 |
|
|
msgbuf_clear(&ibuf_se->w); |
323 |
|
|
free(ibuf_se); |
324 |
|
|
if (ibuf_se_ctl) |
325 |
|
|
msgbuf_clear(&ibuf_se_ctl->w); |
326 |
|
|
free(ibuf_se_ctl); |
327 |
|
|
|
328 |
|
|
msgbuf_clear(&ibuf_main->w); |
329 |
|
|
free(ibuf_main); |
330 |
|
|
|
331 |
|
|
log_info("route decision engine exiting"); |
332 |
|
|
_exit(0); |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
struct network_config netconf_s, netconf_p; |
336 |
|
|
struct filter_set_head *session_set, *parent_set; |
337 |
|
|
|
338 |
|
|
void |
339 |
|
|
rde_dispatch_imsg_session(struct imsgbuf *ibuf) |
340 |
|
|
{ |
341 |
|
|
struct imsg imsg; |
342 |
|
|
struct peer p; |
343 |
|
|
struct peer_config pconf; |
344 |
|
|
struct session_up sup; |
345 |
|
|
struct ctl_show_rib csr; |
346 |
|
|
struct ctl_show_rib_request req; |
347 |
|
|
struct rde_peer *peer; |
348 |
|
|
struct rde_aspath *asp; |
349 |
|
|
struct filter_set *s; |
350 |
|
|
struct nexthop *nh; |
351 |
|
|
u_int8_t *asdata; |
352 |
|
|
ssize_t n; |
353 |
|
|
int verbose; |
354 |
|
|
u_int16_t len; |
355 |
|
|
u_int8_t aid; |
356 |
|
|
|
357 |
|
|
while (ibuf) { |
358 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
359 |
|
|
fatal("rde_dispatch_imsg_session: imsg_get error"); |
360 |
|
|
if (n == 0) |
361 |
|
|
break; |
362 |
|
|
|
363 |
|
|
switch (imsg.hdr.type) { |
364 |
|
|
case IMSG_UPDATE: |
365 |
|
|
rde_update_dispatch(&imsg); |
366 |
|
|
break; |
367 |
|
|
case IMSG_SESSION_ADD: |
368 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(pconf)) |
369 |
|
|
fatalx("incorrect size of session request"); |
370 |
|
|
memcpy(&pconf, imsg.data, sizeof(pconf)); |
371 |
|
|
peer_add(imsg.hdr.peerid, &pconf); |
372 |
|
|
break; |
373 |
|
|
case IMSG_SESSION_UP: |
374 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(sup)) |
375 |
|
|
fatalx("incorrect size of session request"); |
376 |
|
|
memcpy(&sup, imsg.data, sizeof(sup)); |
377 |
|
|
peer_up(imsg.hdr.peerid, &sup); |
378 |
|
|
break; |
379 |
|
|
case IMSG_SESSION_DOWN: |
380 |
|
|
peer_down(imsg.hdr.peerid); |
381 |
|
|
break; |
382 |
|
|
case IMSG_SESSION_STALE: |
383 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { |
384 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
385 |
|
|
break; |
386 |
|
|
} |
387 |
|
|
memcpy(&aid, imsg.data, sizeof(aid)); |
388 |
|
|
if (aid >= AID_MAX) |
389 |
|
|
fatalx("IMSG_SESSION_STALE: bad AID"); |
390 |
|
|
peer_stale(imsg.hdr.peerid, aid); |
391 |
|
|
break; |
392 |
|
|
case IMSG_SESSION_FLUSH: |
393 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { |
394 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
395 |
|
|
break; |
396 |
|
|
} |
397 |
|
|
memcpy(&aid, imsg.data, sizeof(aid)); |
398 |
|
|
if (aid >= AID_MAX) |
399 |
|
|
fatalx("IMSG_SESSION_FLUSH: bad AID"); |
400 |
|
|
if ((peer = peer_get(imsg.hdr.peerid)) == NULL) { |
401 |
|
|
log_warnx("rde_dispatch: unknown peer id %d", |
402 |
|
|
imsg.hdr.peerid); |
403 |
|
|
break; |
404 |
|
|
} |
405 |
|
|
peer_flush(peer, aid); |
406 |
|
|
break; |
407 |
|
|
case IMSG_SESSION_RESTARTED: |
408 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { |
409 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
410 |
|
|
break; |
411 |
|
|
} |
412 |
|
|
memcpy(&aid, imsg.data, sizeof(aid)); |
413 |
|
|
if (aid >= AID_MAX) |
414 |
|
|
fatalx("IMSG_SESSION_RESTARTED: bad AID"); |
415 |
|
|
if ((peer = peer_get(imsg.hdr.peerid)) == NULL) { |
416 |
|
|
log_warnx("rde_dispatch: unknown peer id %d", |
417 |
|
|
imsg.hdr.peerid); |
418 |
|
|
break; |
419 |
|
|
} |
420 |
|
|
if (peer->staletime[aid]) |
421 |
|
|
peer_flush(peer, aid); |
422 |
|
|
break; |
423 |
|
|
case IMSG_REFRESH: |
424 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(aid)) { |
425 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
426 |
|
|
break; |
427 |
|
|
} |
428 |
|
|
memcpy(&aid, imsg.data, sizeof(aid)); |
429 |
|
|
if (aid >= AID_MAX) |
430 |
|
|
fatalx("IMSG_REFRESH: bad AID"); |
431 |
|
|
peer_dump(imsg.hdr.peerid, aid); |
432 |
|
|
break; |
433 |
|
|
case IMSG_NETWORK_ADD: |
434 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
435 |
|
|
sizeof(struct network_config)) { |
436 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
437 |
|
|
break; |
438 |
|
|
} |
439 |
|
|
memcpy(&netconf_s, imsg.data, sizeof(netconf_s)); |
440 |
|
|
TAILQ_INIT(&netconf_s.attrset); |
441 |
|
|
session_set = &netconf_s.attrset; |
442 |
|
|
break; |
443 |
|
|
case IMSG_NETWORK_ASPATH: |
444 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE < |
445 |
|
|
sizeof(struct ctl_show_rib)) { |
446 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
447 |
|
|
bzero(&netconf_s, sizeof(netconf_s)); |
448 |
|
|
break; |
449 |
|
|
} |
450 |
|
|
asdata = imsg.data; |
451 |
|
|
asdata += sizeof(struct ctl_show_rib); |
452 |
|
|
memcpy(&csr, imsg.data, sizeof(csr)); |
453 |
|
|
if (csr.aspath_len + sizeof(csr) > imsg.hdr.len - |
454 |
|
|
IMSG_HEADER_SIZE) { |
455 |
|
|
log_warnx("rde_dispatch: wrong aspath len"); |
456 |
|
|
bzero(&netconf_s, sizeof(netconf_s)); |
457 |
|
|
break; |
458 |
|
|
} |
459 |
|
|
asp = path_get(); |
460 |
|
|
asp->lpref = csr.local_pref; |
461 |
|
|
asp->med = csr.med; |
462 |
|
|
asp->weight = csr.weight; |
463 |
|
|
asp->flags = csr.flags; |
464 |
|
|
asp->origin = csr.origin; |
465 |
|
|
asp->flags |= F_PREFIX_ANNOUNCED | F_ANN_DYNAMIC; |
466 |
|
|
asp->aspath = aspath_get(asdata, csr.aspath_len); |
467 |
|
|
netconf_s.asp = asp; |
468 |
|
|
break; |
469 |
|
|
case IMSG_NETWORK_ATTR: |
470 |
|
|
if (imsg.hdr.len <= IMSG_HEADER_SIZE) { |
471 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
472 |
|
|
break; |
473 |
|
|
} |
474 |
|
|
/* parse path attributes */ |
475 |
|
|
len = imsg.hdr.len - IMSG_HEADER_SIZE; |
476 |
|
|
asp = netconf_s.asp; |
477 |
|
|
if (rde_attr_add(asp, imsg.data, len) == -1) { |
478 |
|
|
log_warnx("rde_dispatch: bad network " |
479 |
|
|
"attribute"); |
480 |
|
|
path_put(asp); |
481 |
|
|
bzero(&netconf_s, sizeof(netconf_s)); |
482 |
|
|
break; |
483 |
|
|
} |
484 |
|
|
break; |
485 |
|
|
case IMSG_NETWORK_DONE: |
486 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE) { |
487 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
488 |
|
|
break; |
489 |
|
|
} |
490 |
|
|
session_set = NULL; |
491 |
|
|
switch (netconf_s.prefix.aid) { |
492 |
|
|
case AID_INET: |
493 |
|
|
if (netconf_s.prefixlen > 32) |
494 |
|
|
goto badnet; |
495 |
|
|
network_add(&netconf_s, 0); |
496 |
|
|
break; |
497 |
|
|
case AID_INET6: |
498 |
|
|
if (netconf_s.prefixlen > 128) |
499 |
|
|
goto badnet; |
500 |
|
|
network_add(&netconf_s, 0); |
501 |
|
|
break; |
502 |
|
|
case 0: |
503 |
|
|
/* something failed beforehands */ |
504 |
|
|
break; |
505 |
|
|
default: |
506 |
|
|
badnet: |
507 |
|
|
log_warnx("rde_dispatch: bad network"); |
508 |
|
|
break; |
509 |
|
|
} |
510 |
|
|
break; |
511 |
|
|
case IMSG_NETWORK_REMOVE: |
512 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
513 |
|
|
sizeof(struct network_config)) { |
514 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
515 |
|
|
break; |
516 |
|
|
} |
517 |
|
|
memcpy(&netconf_s, imsg.data, sizeof(netconf_s)); |
518 |
|
|
TAILQ_INIT(&netconf_s.attrset); |
519 |
|
|
network_delete(&netconf_s, 0); |
520 |
|
|
break; |
521 |
|
|
case IMSG_NETWORK_FLUSH: |
522 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE) { |
523 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
524 |
|
|
break; |
525 |
|
|
} |
526 |
|
|
prefix_network_clean(peerself, time(NULL), |
527 |
|
|
F_ANN_DYNAMIC); |
528 |
|
|
break; |
529 |
|
|
case IMSG_FILTER_SET: |
530 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
531 |
|
|
sizeof(struct filter_set)) { |
532 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
533 |
|
|
break; |
534 |
|
|
} |
535 |
|
|
if (session_set == NULL) { |
536 |
|
|
log_warnx("rde_dispatch: " |
537 |
|
|
"IMSG_FILTER_SET unexpected"); |
538 |
|
|
break; |
539 |
|
|
} |
540 |
|
|
if ((s = malloc(sizeof(struct filter_set))) == NULL) |
541 |
|
|
fatal(NULL); |
542 |
|
|
memcpy(s, imsg.data, sizeof(struct filter_set)); |
543 |
|
|
TAILQ_INSERT_TAIL(session_set, s, entry); |
544 |
|
|
|
545 |
|
|
if (s->type == ACTION_SET_NEXTHOP) { |
546 |
|
|
nh = nexthop_get(&s->action.nexthop); |
547 |
|
|
nh->refcnt++; |
548 |
|
|
} |
549 |
|
|
break; |
550 |
|
|
case IMSG_CTL_SHOW_NETWORK: |
551 |
|
|
case IMSG_CTL_SHOW_RIB: |
552 |
|
|
case IMSG_CTL_SHOW_RIB_AS: |
553 |
|
|
case IMSG_CTL_SHOW_RIB_COMMUNITY: |
554 |
|
|
case IMSG_CTL_SHOW_RIB_PREFIX: |
555 |
|
|
if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(req)) { |
556 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
557 |
|
|
break; |
558 |
|
|
} |
559 |
|
|
memcpy(&req, imsg.data, sizeof(req)); |
560 |
|
|
rde_dump_ctx_new(&req, imsg.hdr.pid, imsg.hdr.type); |
561 |
|
|
break; |
562 |
|
|
case IMSG_CTL_SHOW_NEIGHBOR: |
563 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
564 |
|
|
sizeof(struct peer)) { |
565 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
566 |
|
|
break; |
567 |
|
|
} |
568 |
|
|
memcpy(&p, imsg.data, sizeof(struct peer)); |
569 |
|
|
peer = peer_get(p.conf.id); |
570 |
|
|
if (peer != NULL) { |
571 |
|
|
p.stats.prefix_cnt = peer->prefix_cnt; |
572 |
|
|
p.stats.prefix_rcvd_update = |
573 |
|
|
peer->prefix_rcvd_update; |
574 |
|
|
p.stats.prefix_rcvd_withdraw = |
575 |
|
|
peer->prefix_rcvd_withdraw; |
576 |
|
|
p.stats.prefix_rcvd_eor = |
577 |
|
|
peer->prefix_rcvd_eor; |
578 |
|
|
p.stats.prefix_sent_update = |
579 |
|
|
peer->prefix_sent_update; |
580 |
|
|
p.stats.prefix_sent_withdraw = |
581 |
|
|
peer->prefix_sent_withdraw; |
582 |
|
|
p.stats.prefix_sent_eor = |
583 |
|
|
peer->prefix_sent_eor; |
584 |
|
|
} |
585 |
|
|
imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NEIGHBOR, 0, |
586 |
|
|
imsg.hdr.pid, -1, &p, sizeof(struct peer)); |
587 |
|
|
break; |
588 |
|
|
case IMSG_CTL_END: |
589 |
|
|
imsg_compose(ibuf_se_ctl, IMSG_CTL_END, 0, imsg.hdr.pid, |
590 |
|
|
-1, NULL, 0); |
591 |
|
|
break; |
592 |
|
|
case IMSG_CTL_SHOW_RIB_MEM: |
593 |
|
|
imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_RIB_MEM, 0, |
594 |
|
|
imsg.hdr.pid, -1, &rdemem, sizeof(rdemem)); |
595 |
|
|
break; |
596 |
|
|
case IMSG_CTL_LOG_VERBOSE: |
597 |
|
|
/* already checked by SE */ |
598 |
|
|
memcpy(&verbose, imsg.data, sizeof(verbose)); |
599 |
|
|
log_verbose(verbose); |
600 |
|
|
break; |
601 |
|
|
default: |
602 |
|
|
break; |
603 |
|
|
} |
604 |
|
|
imsg_free(&imsg); |
605 |
|
|
} |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
void |
609 |
|
|
rde_dispatch_imsg_parent(struct imsgbuf *ibuf) |
610 |
|
|
{ |
611 |
|
|
static struct rdomain *rd; |
612 |
|
|
struct imsg imsg; |
613 |
|
|
struct mrt xmrt; |
614 |
|
|
struct rde_rib rn; |
615 |
|
|
struct imsgbuf *i; |
616 |
|
|
struct filter_head *nr; |
617 |
|
|
struct filter_rule *r; |
618 |
|
|
struct filter_set *s; |
619 |
|
|
struct nexthop *nh; |
620 |
|
|
int n, fd; |
621 |
|
|
u_int16_t rid; |
622 |
|
|
|
623 |
|
|
while (ibuf) { |
624 |
|
|
if ((n = imsg_get(ibuf, &imsg)) == -1) |
625 |
|
|
fatal("rde_dispatch_imsg_parent: imsg_get error"); |
626 |
|
|
if (n == 0) |
627 |
|
|
break; |
628 |
|
|
|
629 |
|
|
switch (imsg.hdr.type) { |
630 |
|
|
case IMSG_SOCKET_CONN: |
631 |
|
|
case IMSG_SOCKET_CONN_CTL: |
632 |
|
|
if ((fd = imsg.fd) == -1) { |
633 |
|
|
log_warnx("expected to receive imsg fd to " |
634 |
|
|
"SE but didn't receive any"); |
635 |
|
|
break; |
636 |
|
|
} |
637 |
|
|
if ((i = malloc(sizeof(struct imsgbuf))) == NULL) |
638 |
|
|
fatal(NULL); |
639 |
|
|
imsg_init(i, fd); |
640 |
|
|
if (imsg.hdr.type == IMSG_SOCKET_CONN) { |
641 |
|
|
if (ibuf_se) { |
642 |
|
|
log_warnx("Unexpected imsg connection " |
643 |
|
|
"to SE received"); |
644 |
|
|
msgbuf_clear(&ibuf_se->w); |
645 |
|
|
free(ibuf_se); |
646 |
|
|
} |
647 |
|
|
ibuf_se = i; |
648 |
|
|
} else { |
649 |
|
|
if (ibuf_se_ctl) { |
650 |
|
|
log_warnx("Unexpected imsg ctl " |
651 |
|
|
"connection to SE received"); |
652 |
|
|
msgbuf_clear(&ibuf_se_ctl->w); |
653 |
|
|
free(ibuf_se_ctl); |
654 |
|
|
} |
655 |
|
|
ibuf_se_ctl = i; |
656 |
|
|
} |
657 |
|
|
break; |
658 |
|
|
case IMSG_NETWORK_ADD: |
659 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
660 |
|
|
sizeof(struct network_config)) { |
661 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
662 |
|
|
break; |
663 |
|
|
} |
664 |
|
|
memcpy(&netconf_p, imsg.data, sizeof(netconf_p)); |
665 |
|
|
TAILQ_INIT(&netconf_p.attrset); |
666 |
|
|
parent_set = &netconf_p.attrset; |
667 |
|
|
break; |
668 |
|
|
case IMSG_NETWORK_DONE: |
669 |
|
|
parent_set = NULL; |
670 |
|
|
network_add(&netconf_p, 1); |
671 |
|
|
break; |
672 |
|
|
case IMSG_NETWORK_REMOVE: |
673 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
674 |
|
|
sizeof(struct network_config)) { |
675 |
|
|
log_warnx("rde_dispatch: wrong imsg len"); |
676 |
|
|
break; |
677 |
|
|
} |
678 |
|
|
memcpy(&netconf_p, imsg.data, sizeof(netconf_p)); |
679 |
|
|
TAILQ_INIT(&netconf_p.attrset); |
680 |
|
|
network_delete(&netconf_p, 1); |
681 |
|
|
break; |
682 |
|
|
case IMSG_RECONF_CONF: |
683 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
684 |
|
|
sizeof(struct bgpd_config)) |
685 |
|
|
fatalx("IMSG_RECONF_CONF bad len"); |
686 |
|
|
reloadtime = time(NULL); |
687 |
|
|
out_rules_tmp = calloc(1, sizeof(struct filter_head)); |
688 |
|
|
if (out_rules_tmp == NULL) |
689 |
|
|
fatal(NULL); |
690 |
|
|
TAILQ_INIT(out_rules_tmp); |
691 |
|
|
newdomains = calloc(1, sizeof(struct rdomain_head)); |
692 |
|
|
if (newdomains == NULL) |
693 |
|
|
fatal(NULL); |
694 |
|
|
SIMPLEQ_INIT(newdomains); |
695 |
|
|
if ((nconf = malloc(sizeof(struct bgpd_config))) == |
696 |
|
|
NULL) |
697 |
|
|
fatal(NULL); |
698 |
|
|
memcpy(nconf, imsg.data, sizeof(struct bgpd_config)); |
699 |
|
|
for (rid = 0; rid < rib_size; rid++) { |
700 |
|
|
if (*ribs[rid].name == '\0') |
701 |
|
|
break; |
702 |
|
|
ribs[rid].state = RECONF_DELETE; |
703 |
|
|
} |
704 |
|
|
break; |
705 |
|
|
case IMSG_RECONF_RIB: |
706 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
707 |
|
|
sizeof(struct rde_rib)) |
708 |
|
|
fatalx("IMSG_RECONF_RIB bad len"); |
709 |
|
|
memcpy(&rn, imsg.data, sizeof(rn)); |
710 |
|
|
rid = rib_find(rn.name); |
711 |
|
|
if (rid == RIB_FAILED) |
712 |
|
|
rib_new(rn.name, rn.rtableid, rn.flags); |
713 |
|
|
else if (ribs[rid].rtableid != rn.rtableid || |
714 |
|
|
(ribs[rid].flags & F_RIB_HASNOFIB) != |
715 |
|
|
(rn.flags & F_RIB_HASNOFIB)) { |
716 |
|
|
struct filter_head *in_rules; |
717 |
|
|
/* |
718 |
|
|
* Big hammer in the F_RIB_HASNOFIB case but |
719 |
|
|
* not often enough used to optimise it more. |
720 |
|
|
* Need to save the filters so that they're not |
721 |
|
|
* lost. |
722 |
|
|
*/ |
723 |
|
|
in_rules = ribs[rid].in_rules; |
724 |
|
|
ribs[rid].in_rules = NULL; |
725 |
|
|
rib_free(&ribs[rid]); |
726 |
|
|
rib_new(rn.name, rn.rtableid, rn.flags); |
727 |
|
|
ribs[rid].in_rules = in_rules; |
728 |
|
|
} else |
729 |
|
|
ribs[rid].state = RECONF_KEEP; |
730 |
|
|
break; |
731 |
|
|
case IMSG_RECONF_FILTER: |
732 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
733 |
|
|
sizeof(struct filter_rule)) |
734 |
|
|
fatalx("IMSG_RECONF_FILTER bad len"); |
735 |
|
|
if ((r = malloc(sizeof(struct filter_rule))) == NULL) |
736 |
|
|
fatal(NULL); |
737 |
|
|
memcpy(r, imsg.data, sizeof(struct filter_rule)); |
738 |
|
|
TAILQ_INIT(&r->set); |
739 |
|
|
if ((r->peer.ribid = rib_find(r->rib)) == RIB_FAILED) { |
740 |
|
|
log_warnx("IMSG_RECONF_FILTER: filter rule " |
741 |
|
|
"for nonexistent rib %s", r->rib); |
742 |
|
|
parent_set = NULL; |
743 |
|
|
free(r); |
744 |
|
|
break; |
745 |
|
|
} |
746 |
|
|
parent_set = &r->set; |
747 |
|
|
if (r->dir == DIR_IN) { |
748 |
|
|
nr = ribs[r->peer.ribid].in_rules_tmp; |
749 |
|
|
if (nr == NULL) { |
750 |
|
|
nr = calloc(1, |
751 |
|
|
sizeof(struct filter_head)); |
752 |
|
|
if (nr == NULL) |
753 |
|
|
fatal(NULL); |
754 |
|
|
TAILQ_INIT(nr); |
755 |
|
|
ribs[r->peer.ribid].in_rules_tmp = nr; |
756 |
|
|
} |
757 |
|
|
TAILQ_INSERT_TAIL(nr, r, entry); |
758 |
|
|
} else |
759 |
|
|
TAILQ_INSERT_TAIL(out_rules_tmp, r, entry); |
760 |
|
|
break; |
761 |
|
|
case IMSG_RECONF_RDOMAIN: |
762 |
|
|
if (imsg.hdr.len - IMSG_HEADER_SIZE != |
763 |
|
|
sizeof(struct rdomain)) |
764 |
|
|
fatalx("IMSG_RECONF_RDOMAIN bad len"); |
765 |
|
|
if ((rd = malloc(sizeof(struct rdomain))) == NULL) |
766 |
|
|
fatal(NULL); |
767 |
|
|
memcpy(rd, imsg.data, sizeof(struct rdomain)); |
768 |
|
|
TAILQ_INIT(&rd->import); |
769 |
|
|
TAILQ_INIT(&rd->export); |
770 |
|
|
SIMPLEQ_INSERT_TAIL(newdomains, rd, entry); |
771 |
|
|
break; |
772 |
|
|
case IMSG_RECONF_RDOMAIN_EXPORT: |
773 |
|
|
if (rd == NULL) { |
774 |
|
|
log_warnx("rde_dispatch_imsg_parent: " |
775 |
|
|
"IMSG_RECONF_RDOMAIN_EXPORT unexpected"); |
776 |
|
|
break; |
777 |
|
|
} |
778 |
|
|
parent_set = &rd->export; |
779 |
|
|
break; |
780 |
|
|
case IMSG_RECONF_RDOMAIN_IMPORT: |
781 |
|
|
if (rd == NULL) { |
782 |
|
|
log_warnx("rde_dispatch_imsg_parent: " |
783 |
|
|
"IMSG_RECONF_RDOMAIN_IMPORT unexpected"); |
784 |
|
|
break; |
785 |
|
|
} |
786 |
|
|
parent_set = &rd->import; |
787 |
|
|
break; |
788 |
|
|
case IMSG_RECONF_RDOMAIN_DONE: |
789 |
|
|
parent_set = NULL; |
790 |
|
|
break; |
791 |
|
|
case IMSG_RECONF_DONE: |
792 |
|
|
if (nconf == NULL) |
793 |
|
|
fatalx("got IMSG_RECONF_DONE but no config"); |
794 |
|
|
parent_set = NULL; |
795 |
|
|
|
796 |
|
|
rde_reload_done(); |
797 |
|
|
break; |
798 |
|
|
case IMSG_NEXTHOP_UPDATE: |
799 |
|
|
nexthop_update(imsg.data); |
800 |
|
|
break; |
801 |
|
|
case IMSG_FILTER_SET: |
802 |
|
|
if (imsg.hdr.len > IMSG_HEADER_SIZE + |
803 |
|
|
sizeof(struct filter_set)) |
804 |
|
|
fatalx("IMSG_FILTER_SET bad len"); |
805 |
|
|
if (parent_set == NULL) { |
806 |
|
|
log_warnx("rde_dispatch_imsg_parent: " |
807 |
|
|
"IMSG_FILTER_SET unexpected"); |
808 |
|
|
break; |
809 |
|
|
} |
810 |
|
|
if ((s = malloc(sizeof(struct filter_set))) == NULL) |
811 |
|
|
fatal(NULL); |
812 |
|
|
memcpy(s, imsg.data, sizeof(struct filter_set)); |
813 |
|
|
TAILQ_INSERT_TAIL(parent_set, s, entry); |
814 |
|
|
|
815 |
|
|
if (s->type == ACTION_SET_NEXTHOP) { |
816 |
|
|
nh = nexthop_get(&s->action.nexthop); |
817 |
|
|
nh->refcnt++; |
818 |
|
|
} |
819 |
|
|
break; |
820 |
|
|
case IMSG_MRT_OPEN: |
821 |
|
|
case IMSG_MRT_REOPEN: |
822 |
|
|
if (imsg.hdr.len > IMSG_HEADER_SIZE + |
823 |
|
|
sizeof(struct mrt)) { |
824 |
|
|
log_warnx("wrong imsg len"); |
825 |
|
|
break; |
826 |
|
|
} |
827 |
|
|
memcpy(&xmrt, imsg.data, sizeof(xmrt)); |
828 |
|
|
if ((fd = imsg.fd) == -1) |
829 |
|
|
log_warnx("expected to receive fd for mrt dump " |
830 |
|
|
"but didn't receive any"); |
831 |
|
|
else if (xmrt.type == MRT_TABLE_DUMP || |
832 |
|
|
xmrt.type == MRT_TABLE_DUMP_MP || |
833 |
|
|
xmrt.type == MRT_TABLE_DUMP_V2) { |
834 |
|
|
rde_dump_mrt_new(&xmrt, imsg.hdr.pid, fd); |
835 |
|
|
} else |
836 |
|
|
close(fd); |
837 |
|
|
break; |
838 |
|
|
case IMSG_MRT_CLOSE: |
839 |
|
|
/* ignore end message because a dump is atomic */ |
840 |
|
|
break; |
841 |
|
|
default: |
842 |
|
|
break; |
843 |
|
|
} |
844 |
|
|
imsg_free(&imsg); |
845 |
|
|
} |
846 |
|
|
} |
847 |
|
|
|
848 |
|
|
/* handle routing updates from the session engine. */ |
849 |
|
|
int |
850 |
|
|
rde_update_dispatch(struct imsg *imsg) |
851 |
|
|
{ |
852 |
|
|
struct bgpd_addr prefix; |
853 |
|
|
struct mpattr mpa; |
854 |
|
|
struct rde_peer *peer; |
855 |
|
|
struct rde_aspath *asp = NULL; |
856 |
|
|
u_char *p, *mpp = NULL; |
857 |
|
|
int error = -1, pos = 0; |
858 |
|
|
u_int16_t afi, len, mplen; |
859 |
|
|
u_int16_t withdrawn_len; |
860 |
|
|
u_int16_t attrpath_len; |
861 |
|
|
u_int16_t nlri_len; |
862 |
|
|
u_int8_t aid, prefixlen, safi, subtype; |
863 |
|
|
u_int32_t fas; |
864 |
|
|
|
865 |
|
|
peer = peer_get(imsg->hdr.peerid); |
866 |
|
|
if (peer == NULL) /* unknown peer, cannot happen */ |
867 |
|
|
return (-1); |
868 |
|
|
if (peer->state != PEER_UP) |
869 |
|
|
return (-1); /* peer is not yet up, cannot happen */ |
870 |
|
|
|
871 |
|
|
p = imsg->data; |
872 |
|
|
|
873 |
|
|
if (imsg->hdr.len < IMSG_HEADER_SIZE + 2) { |
874 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); |
875 |
|
|
return (-1); |
876 |
|
|
} |
877 |
|
|
|
878 |
|
|
memcpy(&len, p, 2); |
879 |
|
|
withdrawn_len = ntohs(len); |
880 |
|
|
p += 2; |
881 |
|
|
if (imsg->hdr.len < IMSG_HEADER_SIZE + 2 + withdrawn_len + 2) { |
882 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); |
883 |
|
|
return (-1); |
884 |
|
|
} |
885 |
|
|
|
886 |
|
|
p += withdrawn_len; |
887 |
|
|
memcpy(&len, p, 2); |
888 |
|
|
attrpath_len = len = ntohs(len); |
889 |
|
|
p += 2; |
890 |
|
|
if (imsg->hdr.len < |
891 |
|
|
IMSG_HEADER_SIZE + 2 + withdrawn_len + 2 + attrpath_len) { |
892 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, NULL, 0); |
893 |
|
|
return (-1); |
894 |
|
|
} |
895 |
|
|
|
896 |
|
|
nlri_len = |
897 |
|
|
imsg->hdr.len - IMSG_HEADER_SIZE - 4 - withdrawn_len - attrpath_len; |
898 |
|
|
bzero(&mpa, sizeof(mpa)); |
899 |
|
|
|
900 |
|
|
if (attrpath_len != 0) { /* 0 = no NLRI information in this message */ |
901 |
|
|
/* parse path attributes */ |
902 |
|
|
asp = path_get(); |
903 |
|
|
while (len > 0) { |
904 |
|
|
if ((pos = rde_attr_parse(p, len, peer, asp, |
905 |
|
|
&mpa)) < 0) |
906 |
|
|
goto done; |
907 |
|
|
p += pos; |
908 |
|
|
len -= pos; |
909 |
|
|
} |
910 |
|
|
|
911 |
|
|
/* check for missing but necessary attributes */ |
912 |
|
|
if ((subtype = rde_attr_missing(asp, peer->conf.ebgp, |
913 |
|
|
nlri_len))) { |
914 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_MISSNG_WK_ATTR, |
915 |
|
|
&subtype, sizeof(u_int8_t)); |
916 |
|
|
goto done; |
917 |
|
|
} |
918 |
|
|
|
919 |
|
|
rde_as4byte_fixup(peer, asp); |
920 |
|
|
|
921 |
|
|
/* enforce remote AS if requested */ |
922 |
|
|
if (asp->flags & F_ATTR_ASPATH && |
923 |
|
|
peer->conf.enforce_as == ENFORCE_AS_ON) { |
924 |
|
|
fas = aspath_neighbor(asp->aspath); |
925 |
|
|
if (peer->conf.remote_as != fas) { |
926 |
|
|
log_peer_warnx(&peer->conf, "bad path, " |
927 |
|
|
"starting with %s, " |
928 |
|
|
"enforce neighbor-as enabled", log_as(fas)); |
929 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, |
930 |
|
|
NULL, 0); |
931 |
|
|
goto done; |
932 |
|
|
} |
933 |
|
|
} |
934 |
|
|
|
935 |
|
|
rde_reflector(peer, asp); |
936 |
|
|
} |
937 |
|
|
|
938 |
|
|
p = imsg->data; |
939 |
|
|
len = withdrawn_len; |
940 |
|
|
p += 2; |
941 |
|
|
/* withdraw prefix */ |
942 |
|
|
while (len > 0) { |
943 |
|
|
if ((pos = rde_update_get_prefix(p, len, &prefix, |
944 |
|
|
&prefixlen)) == -1) { |
945 |
|
|
/* |
946 |
|
|
* the RFC does not mention what we should do in |
947 |
|
|
* this case. Let's do the same as in the NLRI case. |
948 |
|
|
*/ |
949 |
|
|
log_peer_warnx(&peer->conf, "bad withdraw prefix"); |
950 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, |
951 |
|
|
NULL, 0); |
952 |
|
|
goto done; |
953 |
|
|
} |
954 |
|
|
if (prefixlen > 32) { |
955 |
|
|
log_peer_warnx(&peer->conf, "bad withdraw prefix"); |
956 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, |
957 |
|
|
NULL, 0); |
958 |
|
|
goto done; |
959 |
|
|
} |
960 |
|
|
|
961 |
|
|
p += pos; |
962 |
|
|
len -= pos; |
963 |
|
|
|
964 |
|
|
if (peer->capa.mp[AID_INET] == 0) { |
965 |
|
|
log_peer_warnx(&peer->conf, |
966 |
|
|
"bad withdraw, %s disabled", aid2str(AID_INET)); |
967 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
968 |
|
|
NULL, 0); |
969 |
|
|
goto done; |
970 |
|
|
} |
971 |
|
|
|
972 |
|
|
rde_update_withdraw(peer, &prefix, prefixlen); |
973 |
|
|
} |
974 |
|
|
|
975 |
|
|
if (attrpath_len == 0) { |
976 |
|
|
/* 0 = no NLRI information in this message */ |
977 |
|
|
if (nlri_len != 0) { |
978 |
|
|
/* crap at end of update which should not be there */ |
979 |
|
|
rde_update_err(peer, ERR_UPDATE, |
980 |
|
|
ERR_UPD_ATTRLIST, NULL, 0); |
981 |
|
|
return (-1); |
982 |
|
|
} |
983 |
|
|
if (withdrawn_len == 0) { |
984 |
|
|
/* EoR marker */ |
985 |
|
|
peer_recv_eor(peer, AID_INET); |
986 |
|
|
} |
987 |
|
|
return (0); |
988 |
|
|
} |
989 |
|
|
|
990 |
|
|
/* withdraw MP_UNREACH_NLRI if available */ |
991 |
|
|
if (mpa.unreach_len != 0) { |
992 |
|
|
mpp = mpa.unreach; |
993 |
|
|
mplen = mpa.unreach_len; |
994 |
|
|
memcpy(&afi, mpp, 2); |
995 |
|
|
mpp += 2; |
996 |
|
|
mplen -= 2; |
997 |
|
|
afi = ntohs(afi); |
998 |
|
|
safi = *mpp++; |
999 |
|
|
mplen--; |
1000 |
|
|
|
1001 |
|
|
if (afi2aid(afi, safi, &aid) == -1) { |
1002 |
|
|
log_peer_warnx(&peer->conf, |
1003 |
|
|
"bad AFI/SAFI pair in withdraw"); |
1004 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
1005 |
|
|
NULL, 0); |
1006 |
|
|
goto done; |
1007 |
|
|
} |
1008 |
|
|
|
1009 |
|
|
if (peer->capa.mp[aid] == 0) { |
1010 |
|
|
log_peer_warnx(&peer->conf, |
1011 |
|
|
"bad withdraw, %s disabled", aid2str(aid)); |
1012 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
1013 |
|
|
NULL, 0); |
1014 |
|
|
goto done; |
1015 |
|
|
} |
1016 |
|
|
|
1017 |
|
|
if ((asp->flags & ~F_ATTR_MP_UNREACH) == 0 && mplen == 0) { |
1018 |
|
|
/* EoR marker */ |
1019 |
|
|
peer_recv_eor(peer, aid); |
1020 |
|
|
} |
1021 |
|
|
|
1022 |
|
|
switch (aid) { |
1023 |
|
|
case AID_INET6: |
1024 |
|
|
while (mplen > 0) { |
1025 |
|
|
if ((pos = rde_update_get_prefix6(mpp, mplen, |
1026 |
|
|
&prefix, &prefixlen)) == -1) { |
1027 |
|
|
log_peer_warnx(&peer->conf, |
1028 |
|
|
"bad IPv6 withdraw prefix"); |
1029 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1030 |
|
|
ERR_UPD_OPTATTR, |
1031 |
|
|
mpa.unreach, mpa.unreach_len); |
1032 |
|
|
goto done; |
1033 |
|
|
} |
1034 |
|
|
if (prefixlen > 128) { |
1035 |
|
|
log_peer_warnx(&peer->conf, |
1036 |
|
|
"bad IPv6 withdraw prefix"); |
1037 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1038 |
|
|
ERR_UPD_OPTATTR, |
1039 |
|
|
mpa.unreach, mpa.unreach_len); |
1040 |
|
|
goto done; |
1041 |
|
|
} |
1042 |
|
|
|
1043 |
|
|
mpp += pos; |
1044 |
|
|
mplen -= pos; |
1045 |
|
|
|
1046 |
|
|
rde_update_withdraw(peer, &prefix, prefixlen); |
1047 |
|
|
} |
1048 |
|
|
break; |
1049 |
|
|
case AID_VPN_IPv4: |
1050 |
|
|
while (mplen > 0) { |
1051 |
|
|
if ((pos = rde_update_get_vpn4(mpp, mplen, |
1052 |
|
|
&prefix, &prefixlen)) == -1) { |
1053 |
|
|
log_peer_warnx(&peer->conf, |
1054 |
|
|
"bad VPNv4 withdraw prefix"); |
1055 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1056 |
|
|
ERR_UPD_OPTATTR, |
1057 |
|
|
mpa.unreach, mpa.unreach_len); |
1058 |
|
|
goto done; |
1059 |
|
|
} |
1060 |
|
|
if (prefixlen > 32) { |
1061 |
|
|
log_peer_warnx(&peer->conf, |
1062 |
|
|
"bad VPNv4 withdraw prefix"); |
1063 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1064 |
|
|
ERR_UPD_OPTATTR, |
1065 |
|
|
mpa.unreach, mpa.unreach_len); |
1066 |
|
|
goto done; |
1067 |
|
|
} |
1068 |
|
|
|
1069 |
|
|
mpp += pos; |
1070 |
|
|
mplen -= pos; |
1071 |
|
|
|
1072 |
|
|
rde_update_withdraw(peer, &prefix, prefixlen); |
1073 |
|
|
} |
1074 |
|
|
break; |
1075 |
|
|
default: |
1076 |
|
|
/* silently ignore unsupported multiprotocol AF */ |
1077 |
|
|
break; |
1078 |
|
|
} |
1079 |
|
|
|
1080 |
|
|
if ((asp->flags & ~F_ATTR_MP_UNREACH) == 0) { |
1081 |
|
|
error = 0; |
1082 |
|
|
goto done; |
1083 |
|
|
} |
1084 |
|
|
} |
1085 |
|
|
|
1086 |
|
|
/* shift to NLRI information */ |
1087 |
|
|
p += 2 + attrpath_len; |
1088 |
|
|
|
1089 |
|
|
/* aspath needs to be loop free nota bene this is not a hard error */ |
1090 |
|
|
if (peer->conf.ebgp && !aspath_loopfree(asp->aspath, conf->as)) |
1091 |
|
|
asp->flags |= F_ATTR_LOOP; |
1092 |
|
|
|
1093 |
|
|
/* parse nlri prefix */ |
1094 |
|
|
while (nlri_len > 0) { |
1095 |
|
|
if ((pos = rde_update_get_prefix(p, nlri_len, &prefix, |
1096 |
|
|
&prefixlen)) == -1) { |
1097 |
|
|
log_peer_warnx(&peer->conf, "bad nlri prefix"); |
1098 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, |
1099 |
|
|
NULL, 0); |
1100 |
|
|
goto done; |
1101 |
|
|
} |
1102 |
|
|
if (prefixlen > 32) { |
1103 |
|
|
log_peer_warnx(&peer->conf, "bad nlri prefix"); |
1104 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, |
1105 |
|
|
NULL, 0); |
1106 |
|
|
goto done; |
1107 |
|
|
} |
1108 |
|
|
|
1109 |
|
|
p += pos; |
1110 |
|
|
nlri_len -= pos; |
1111 |
|
|
|
1112 |
|
|
if (peer->capa.mp[AID_INET] == 0) { |
1113 |
|
|
log_peer_warnx(&peer->conf, |
1114 |
|
|
"bad update, %s disabled", aid2str(AID_INET)); |
1115 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
1116 |
|
|
NULL, 0); |
1117 |
|
|
goto done; |
1118 |
|
|
} |
1119 |
|
|
|
1120 |
|
|
rde_update_update(peer, asp, &prefix, prefixlen); |
1121 |
|
|
|
1122 |
|
|
/* max prefix checker */ |
1123 |
|
|
if (peer->conf.max_prefix && |
1124 |
|
|
peer->prefix_cnt >= peer->conf.max_prefix) { |
1125 |
|
|
log_peer_warnx(&peer->conf, "prefix limit reached" |
1126 |
|
|
" (>%u/%u)", peer->prefix_cnt, peer->conf.max_prefix); |
1127 |
|
|
rde_update_err(peer, ERR_CEASE, ERR_CEASE_MAX_PREFIX, |
1128 |
|
|
NULL, 0); |
1129 |
|
|
goto done; |
1130 |
|
|
} |
1131 |
|
|
|
1132 |
|
|
} |
1133 |
|
|
|
1134 |
|
|
/* add MP_REACH_NLRI if available */ |
1135 |
|
|
if (mpa.reach_len != 0) { |
1136 |
|
|
mpp = mpa.reach; |
1137 |
|
|
mplen = mpa.reach_len; |
1138 |
|
|
memcpy(&afi, mpp, 2); |
1139 |
|
|
mpp += 2; |
1140 |
|
|
mplen -= 2; |
1141 |
|
|
afi = ntohs(afi); |
1142 |
|
|
safi = *mpp++; |
1143 |
|
|
mplen--; |
1144 |
|
|
|
1145 |
|
|
if (afi2aid(afi, safi, &aid) == -1) { |
1146 |
|
|
log_peer_warnx(&peer->conf, |
1147 |
|
|
"bad AFI/SAFI pair in update"); |
1148 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
1149 |
|
|
NULL, 0); |
1150 |
|
|
goto done; |
1151 |
|
|
} |
1152 |
|
|
|
1153 |
|
|
if (peer->capa.mp[aid] == 0) { |
1154 |
|
|
log_peer_warnx(&peer->conf, |
1155 |
|
|
"bad update, %s disabled", aid2str(aid)); |
1156 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
1157 |
|
|
NULL, 0); |
1158 |
|
|
goto done; |
1159 |
|
|
} |
1160 |
|
|
|
1161 |
|
|
/* |
1162 |
|
|
* this works because asp is not linked. |
1163 |
|
|
* But first unlock the previously locked nexthop. |
1164 |
|
|
*/ |
1165 |
|
|
if (asp->nexthop) { |
1166 |
|
|
asp->nexthop->refcnt--; |
1167 |
|
|
(void)nexthop_delete(asp->nexthop); |
1168 |
|
|
asp->nexthop = NULL; |
1169 |
|
|
} |
1170 |
|
|
if ((pos = rde_get_mp_nexthop(mpp, mplen, aid, asp)) == -1) { |
1171 |
|
|
log_peer_warnx(&peer->conf, "bad nlri prefix"); |
1172 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_OPTATTR, |
1173 |
|
|
mpa.reach, mpa.reach_len); |
1174 |
|
|
goto done; |
1175 |
|
|
} |
1176 |
|
|
mpp += pos; |
1177 |
|
|
mplen -= pos; |
1178 |
|
|
|
1179 |
|
|
switch (aid) { |
1180 |
|
|
case AID_INET6: |
1181 |
|
|
while (mplen > 0) { |
1182 |
|
|
if ((pos = rde_update_get_prefix6(mpp, mplen, |
1183 |
|
|
&prefix, &prefixlen)) == -1) { |
1184 |
|
|
log_peer_warnx(&peer->conf, |
1185 |
|
|
"bad IPv6 nlri prefix"); |
1186 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1187 |
|
|
ERR_UPD_OPTATTR, |
1188 |
|
|
mpa.reach, mpa.reach_len); |
1189 |
|
|
goto done; |
1190 |
|
|
} |
1191 |
|
|
if (prefixlen > 128) { |
1192 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1193 |
|
|
ERR_UPD_OPTATTR, |
1194 |
|
|
mpa.reach, mpa.reach_len); |
1195 |
|
|
goto done; |
1196 |
|
|
} |
1197 |
|
|
|
1198 |
|
|
mpp += pos; |
1199 |
|
|
mplen -= pos; |
1200 |
|
|
|
1201 |
|
|
rde_update_update(peer, asp, &prefix, |
1202 |
|
|
prefixlen); |
1203 |
|
|
|
1204 |
|
|
/* max prefix checker */ |
1205 |
|
|
if (peer->conf.max_prefix && |
1206 |
|
|
peer->prefix_cnt >= peer->conf.max_prefix) { |
1207 |
|
|
log_peer_warnx(&peer->conf, |
1208 |
|
|
"prefix limit reached" |
1209 |
|
|
" (>%u/%u)", peer->prefix_cnt, |
1210 |
|
|
peer->conf.max_prefix); |
1211 |
|
|
rde_update_err(peer, ERR_CEASE, |
1212 |
|
|
ERR_CEASE_MAX_PREFIX, NULL, 0); |
1213 |
|
|
goto done; |
1214 |
|
|
} |
1215 |
|
|
|
1216 |
|
|
} |
1217 |
|
|
break; |
1218 |
|
|
case AID_VPN_IPv4: |
1219 |
|
|
while (mplen > 0) { |
1220 |
|
|
if ((pos = rde_update_get_vpn4(mpp, mplen, |
1221 |
|
|
&prefix, &prefixlen)) == -1) { |
1222 |
|
|
log_peer_warnx(&peer->conf, |
1223 |
|
|
"bad VPNv4 nlri prefix"); |
1224 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1225 |
|
|
ERR_UPD_OPTATTR, |
1226 |
|
|
mpa.reach, mpa.reach_len); |
1227 |
|
|
goto done; |
1228 |
|
|
} |
1229 |
|
|
if (prefixlen > 32) { |
1230 |
|
|
rde_update_err(peer, ERR_UPDATE, |
1231 |
|
|
ERR_UPD_OPTATTR, |
1232 |
|
|
mpa.reach, mpa.reach_len); |
1233 |
|
|
goto done; |
1234 |
|
|
} |
1235 |
|
|
|
1236 |
|
|
mpp += pos; |
1237 |
|
|
mplen -= pos; |
1238 |
|
|
|
1239 |
|
|
rde_update_update(peer, asp, &prefix, |
1240 |
|
|
prefixlen); |
1241 |
|
|
|
1242 |
|
|
/* max prefix checker */ |
1243 |
|
|
if (peer->conf.max_prefix && |
1244 |
|
|
peer->prefix_cnt >= peer->conf.max_prefix) { |
1245 |
|
|
log_peer_warnx(&peer->conf, |
1246 |
|
|
"prefix limit reached" |
1247 |
|
|
" (>%u/%u)", peer->prefix_cnt, |
1248 |
|
|
peer->conf.max_prefix); |
1249 |
|
|
rde_update_err(peer, ERR_CEASE, |
1250 |
|
|
ERR_CEASE_MAX_PREFIX, NULL, 0); |
1251 |
|
|
goto done; |
1252 |
|
|
} |
1253 |
|
|
|
1254 |
|
|
} |
1255 |
|
|
break; |
1256 |
|
|
default: |
1257 |
|
|
/* silently ignore unsupported multiprotocol AF */ |
1258 |
|
|
break; |
1259 |
|
|
} |
1260 |
|
|
} |
1261 |
|
|
|
1262 |
|
|
done: |
1263 |
|
|
if (attrpath_len != 0) { |
1264 |
|
|
/* unlock the previously locked entry */ |
1265 |
|
|
if (asp->nexthop) { |
1266 |
|
|
asp->nexthop->refcnt--; |
1267 |
|
|
(void)nexthop_delete(asp->nexthop); |
1268 |
|
|
} |
1269 |
|
|
/* free allocated attribute memory that is no longer used */ |
1270 |
|
|
path_put(asp); |
1271 |
|
|
} |
1272 |
|
|
|
1273 |
|
|
return (error); |
1274 |
|
|
} |
1275 |
|
|
|
1276 |
|
|
void |
1277 |
|
|
rde_update_update(struct rde_peer *peer, struct rde_aspath *asp, |
1278 |
|
|
struct bgpd_addr *prefix, u_int8_t prefixlen) |
1279 |
|
|
{ |
1280 |
|
|
struct rde_aspath *fasp; |
1281 |
|
|
enum filter_actions action; |
1282 |
|
|
int r = 0, f = 0; |
1283 |
|
|
u_int16_t i; |
1284 |
|
|
|
1285 |
|
|
peer->prefix_rcvd_update++; |
1286 |
|
|
/* add original path to the Adj-RIB-In */ |
1287 |
|
|
if (peer->conf.softreconfig_in) |
1288 |
|
|
r += path_update(&ribs[0], peer, asp, prefix, prefixlen); |
1289 |
|
|
|
1290 |
|
|
for (i = 1; i < rib_size; i++) { |
1291 |
|
|
if (*ribs[i].name == '\0') |
1292 |
|
|
break; |
1293 |
|
|
/* input filter */ |
1294 |
|
|
action = rde_filter(ribs[i].in_rules, &fasp, peer, asp, prefix, |
1295 |
|
|
prefixlen, peer); |
1296 |
|
|
|
1297 |
|
|
if (fasp == NULL) |
1298 |
|
|
fasp = asp; |
1299 |
|
|
|
1300 |
|
|
if (action == ACTION_ALLOW) { |
1301 |
|
|
rde_update_log("update", i, peer, |
1302 |
|
|
&fasp->nexthop->exit_nexthop, prefix, prefixlen); |
1303 |
|
|
r += path_update(&ribs[i], peer, fasp, prefix, |
1304 |
|
|
prefixlen); |
1305 |
|
|
} else if (prefix_remove(&ribs[i], peer, prefix, prefixlen, |
1306 |
|
|
0)) { |
1307 |
|
|
rde_update_log("filtered withdraw", i, peer, |
1308 |
|
|
NULL, prefix, prefixlen); |
1309 |
|
|
f++; |
1310 |
|
|
} |
1311 |
|
|
|
1312 |
|
|
/* free modified aspath */ |
1313 |
|
|
if (fasp != asp) |
1314 |
|
|
path_put(fasp); |
1315 |
|
|
} |
1316 |
|
|
|
1317 |
|
|
if (r) |
1318 |
|
|
peer->prefix_cnt++; |
1319 |
|
|
else if (f) |
1320 |
|
|
peer->prefix_cnt--; |
1321 |
|
|
} |
1322 |
|
|
|
1323 |
|
|
void |
1324 |
|
|
rde_update_withdraw(struct rde_peer *peer, struct bgpd_addr *prefix, |
1325 |
|
|
u_int8_t prefixlen) |
1326 |
|
|
{ |
1327 |
|
|
int r = 0; |
1328 |
|
|
u_int16_t i; |
1329 |
|
|
|
1330 |
|
|
peer->prefix_rcvd_withdraw++; |
1331 |
|
|
|
1332 |
|
|
for (i = rib_size - 1; ; i--) { |
1333 |
|
|
if (*ribs[i].name == '\0') |
1334 |
|
|
break; |
1335 |
|
|
if (prefix_remove(&ribs[i], peer, prefix, prefixlen, 0)) { |
1336 |
|
|
rde_update_log("withdraw", i, peer, NULL, prefix, |
1337 |
|
|
prefixlen); |
1338 |
|
|
r++; |
1339 |
|
|
} |
1340 |
|
|
if (i == 0) |
1341 |
|
|
break; |
1342 |
|
|
} |
1343 |
|
|
|
1344 |
|
|
if (r) |
1345 |
|
|
peer->prefix_cnt--; |
1346 |
|
|
} |
1347 |
|
|
|
1348 |
|
|
/* |
1349 |
|
|
* BGP UPDATE parser functions |
1350 |
|
|
*/ |
1351 |
|
|
|
1352 |
|
|
/* attribute parser specific makros */ |
1353 |
|
|
#define UPD_READ(t, p, plen, n) \ |
1354 |
|
|
do { \ |
1355 |
|
|
memcpy(t, p, n); \ |
1356 |
|
|
p += n; \ |
1357 |
|
|
plen += n; \ |
1358 |
|
|
} while (0) |
1359 |
|
|
|
1360 |
|
|
#define CHECK_FLAGS(s, t, m) \ |
1361 |
|
|
(((s) & ~(ATTR_DEFMASK | (m))) == (t)) |
1362 |
|
|
|
1363 |
|
|
int |
1364 |
|
|
rde_attr_parse(u_char *p, u_int16_t len, struct rde_peer *peer, |
1365 |
|
|
struct rde_aspath *a, struct mpattr *mpa) |
1366 |
|
|
{ |
1367 |
|
|
struct bgpd_addr nexthop; |
1368 |
|
|
u_char *op = p, *npath; |
1369 |
|
|
u_int32_t tmp32; |
1370 |
|
|
int error; |
1371 |
|
|
u_int16_t attr_len, nlen; |
1372 |
|
|
u_int16_t plen = 0; |
1373 |
|
|
u_int8_t flags; |
1374 |
|
|
u_int8_t type; |
1375 |
|
|
u_int8_t tmp8; |
1376 |
|
|
|
1377 |
|
|
if (len < 3) { |
1378 |
|
|
bad_len: |
1379 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLEN, op, len); |
1380 |
|
|
return (-1); |
1381 |
|
|
} |
1382 |
|
|
|
1383 |
|
|
UPD_READ(&flags, p, plen, 1); |
1384 |
|
|
UPD_READ(&type, p, plen, 1); |
1385 |
|
|
|
1386 |
|
|
if (flags & ATTR_EXTLEN) { |
1387 |
|
|
if (len - plen < 2) |
1388 |
|
|
goto bad_len; |
1389 |
|
|
UPD_READ(&attr_len, p, plen, 2); |
1390 |
|
|
attr_len = ntohs(attr_len); |
1391 |
|
|
} else { |
1392 |
|
|
UPD_READ(&tmp8, p, plen, 1); |
1393 |
|
|
attr_len = tmp8; |
1394 |
|
|
} |
1395 |
|
|
|
1396 |
|
|
if (len - plen < attr_len) |
1397 |
|
|
goto bad_len; |
1398 |
|
|
|
1399 |
|
|
/* adjust len to the actual attribute size including header */ |
1400 |
|
|
len = plen + attr_len; |
1401 |
|
|
|
1402 |
|
|
switch (type) { |
1403 |
|
|
case ATTR_UNDEF: |
1404 |
|
|
/* ignore and drop path attributes with a type code of 0 */ |
1405 |
|
|
plen += attr_len; |
1406 |
|
|
break; |
1407 |
|
|
case ATTR_ORIGIN: |
1408 |
|
|
if (attr_len != 1) |
1409 |
|
|
goto bad_len; |
1410 |
|
|
|
1411 |
|
|
if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) { |
1412 |
|
|
bad_flags: |
1413 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRFLAGS, |
1414 |
|
|
op, len); |
1415 |
|
|
return (-1); |
1416 |
|
|
} |
1417 |
|
|
|
1418 |
|
|
UPD_READ(&a->origin, p, plen, 1); |
1419 |
|
|
if (a->origin > ORIGIN_INCOMPLETE) { |
1420 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ORIGIN, |
1421 |
|
|
op, len); |
1422 |
|
|
return (-1); |
1423 |
|
|
} |
1424 |
|
|
if (a->flags & F_ATTR_ORIGIN) |
1425 |
|
|
goto bad_list; |
1426 |
|
|
a->flags |= F_ATTR_ORIGIN; |
1427 |
|
|
break; |
1428 |
|
|
case ATTR_ASPATH: |
1429 |
|
|
if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) |
1430 |
|
|
goto bad_flags; |
1431 |
|
|
error = aspath_verify(p, attr_len, rde_as4byte(peer)); |
1432 |
|
|
if (error == AS_ERR_SOFT) { |
1433 |
|
|
/* |
1434 |
|
|
* soft errors like unexpected segment types are |
1435 |
|
|
* not considered fatal and the path is just |
1436 |
|
|
* marked invalid. |
1437 |
|
|
*/ |
1438 |
|
|
a->flags |= F_ATTR_PARSE_ERR; |
1439 |
|
|
log_peer_warnx(&peer->conf, "bad ASPATH, " |
1440 |
|
|
"path invalidated and prefix withdrawn"); |
1441 |
|
|
} else if (error != 0) { |
1442 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, |
1443 |
|
|
NULL, 0); |
1444 |
|
|
return (-1); |
1445 |
|
|
} |
1446 |
|
|
if (a->flags & F_ATTR_ASPATH) |
1447 |
|
|
goto bad_list; |
1448 |
|
|
if (rde_as4byte(peer)) { |
1449 |
|
|
npath = p; |
1450 |
|
|
nlen = attr_len; |
1451 |
|
|
} else |
1452 |
|
|
npath = aspath_inflate(p, attr_len, &nlen); |
1453 |
|
|
a->flags |= F_ATTR_ASPATH; |
1454 |
|
|
a->aspath = aspath_get(npath, nlen); |
1455 |
|
|
if (npath != p) |
1456 |
|
|
free(npath); |
1457 |
|
|
plen += attr_len; |
1458 |
|
|
break; |
1459 |
|
|
case ATTR_NEXTHOP: |
1460 |
|
|
if (attr_len != 4) |
1461 |
|
|
goto bad_len; |
1462 |
|
|
if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) |
1463 |
|
|
goto bad_flags; |
1464 |
|
|
if (a->flags & F_ATTR_NEXTHOP) |
1465 |
|
|
goto bad_list; |
1466 |
|
|
a->flags |= F_ATTR_NEXTHOP; |
1467 |
|
|
|
1468 |
|
|
bzero(&nexthop, sizeof(nexthop)); |
1469 |
|
|
nexthop.aid = AID_INET; |
1470 |
|
|
UPD_READ(&nexthop.v4.s_addr, p, plen, 4); |
1471 |
|
|
/* |
1472 |
|
|
* Check if the nexthop is a valid IP address. We consider |
1473 |
|
|
* multicast and experimental addresses as invalid. |
1474 |
|
|
*/ |
1475 |
|
|
tmp32 = ntohl(nexthop.v4.s_addr); |
1476 |
|
|
if (IN_MULTICAST(tmp32) || IN_BADCLASS(tmp32)) { |
1477 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_NETWORK, |
1478 |
|
|
op, len); |
1479 |
|
|
return (-1); |
1480 |
|
|
} |
1481 |
|
|
a->nexthop = nexthop_get(&nexthop); |
1482 |
|
|
/* |
1483 |
|
|
* lock the nexthop because it is not yet linked else |
1484 |
|
|
* withdraws may remove this nexthop which in turn would |
1485 |
|
|
* cause a use after free error. |
1486 |
|
|
*/ |
1487 |
|
|
a->nexthop->refcnt++; |
1488 |
|
|
break; |
1489 |
|
|
case ATTR_MED: |
1490 |
|
|
if (attr_len != 4) |
1491 |
|
|
goto bad_len; |
1492 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) |
1493 |
|
|
goto bad_flags; |
1494 |
|
|
if (a->flags & F_ATTR_MED) |
1495 |
|
|
goto bad_list; |
1496 |
|
|
a->flags |= F_ATTR_MED; |
1497 |
|
|
|
1498 |
|
|
UPD_READ(&tmp32, p, plen, 4); |
1499 |
|
|
a->med = ntohl(tmp32); |
1500 |
|
|
break; |
1501 |
|
|
case ATTR_LOCALPREF: |
1502 |
|
|
if (attr_len != 4) |
1503 |
|
|
goto bad_len; |
1504 |
|
|
if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) |
1505 |
|
|
goto bad_flags; |
1506 |
|
|
if (peer->conf.ebgp) { |
1507 |
|
|
/* ignore local-pref attr on non ibgp peers */ |
1508 |
|
|
plen += 4; |
1509 |
|
|
break; |
1510 |
|
|
} |
1511 |
|
|
if (a->flags & F_ATTR_LOCALPREF) |
1512 |
|
|
goto bad_list; |
1513 |
|
|
a->flags |= F_ATTR_LOCALPREF; |
1514 |
|
|
|
1515 |
|
|
UPD_READ(&tmp32, p, plen, 4); |
1516 |
|
|
a->lpref = ntohl(tmp32); |
1517 |
|
|
break; |
1518 |
|
|
case ATTR_ATOMIC_AGGREGATE: |
1519 |
|
|
if (attr_len != 0) |
1520 |
|
|
goto bad_len; |
1521 |
|
|
if (!CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0)) |
1522 |
|
|
goto bad_flags; |
1523 |
|
|
goto optattr; |
1524 |
|
|
case ATTR_AGGREGATOR: |
1525 |
|
|
if ((!rde_as4byte(peer) && attr_len != 6) || |
1526 |
|
|
(rde_as4byte(peer) && attr_len != 8)) { |
1527 |
|
|
/* |
1528 |
|
|
* ignore attribute in case of error as per |
1529 |
|
|
* draft-ietf-idr-optional-transitive-00.txt |
1530 |
|
|
* but only if partial bit is set |
1531 |
|
|
*/ |
1532 |
|
|
if ((flags & ATTR_PARTIAL) == 0) |
1533 |
|
|
goto bad_len; |
1534 |
|
|
log_peer_warnx(&peer->conf, "bad AGGREGATOR, " |
1535 |
|
|
"partial attribute ignored"); |
1536 |
|
|
plen += attr_len; |
1537 |
|
|
break; |
1538 |
|
|
} |
1539 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, |
1540 |
|
|
ATTR_PARTIAL)) |
1541 |
|
|
goto bad_flags; |
1542 |
|
|
if (!rde_as4byte(peer)) { |
1543 |
|
|
/* need to inflate aggregator AS to 4-byte */ |
1544 |
|
|
u_char t[8]; |
1545 |
|
|
t[0] = t[1] = 0; |
1546 |
|
|
UPD_READ(&t[2], p, plen, 2); |
1547 |
|
|
UPD_READ(&t[4], p, plen, 4); |
1548 |
|
|
if (attr_optadd(a, flags, type, t, |
1549 |
|
|
sizeof(t)) == -1) |
1550 |
|
|
goto bad_list; |
1551 |
|
|
break; |
1552 |
|
|
} |
1553 |
|
|
/* 4-byte ready server take the default route */ |
1554 |
|
|
goto optattr; |
1555 |
|
|
case ATTR_COMMUNITIES: |
1556 |
|
|
if (attr_len % 4 != 0) { |
1557 |
|
|
/* |
1558 |
|
|
* mark update as bad and withdraw all routes as per |
1559 |
|
|
* draft-ietf-idr-optional-transitive-00.txt |
1560 |
|
|
* but only if partial bit is set |
1561 |
|
|
*/ |
1562 |
|
|
if ((flags & ATTR_PARTIAL) == 0) |
1563 |
|
|
goto bad_len; |
1564 |
|
|
a->flags |= F_ATTR_PARSE_ERR; |
1565 |
|
|
log_peer_warnx(&peer->conf, "bad COMMUNITIES, " |
1566 |
|
|
"path invalidated and prefix withdrawn"); |
1567 |
|
|
} |
1568 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, |
1569 |
|
|
ATTR_PARTIAL)) |
1570 |
|
|
goto bad_flags; |
1571 |
|
|
goto optattr; |
1572 |
|
|
case ATTR_EXT_COMMUNITIES: |
1573 |
|
|
if (attr_len % 8 != 0) { |
1574 |
|
|
/* |
1575 |
|
|
* mark update as bad and withdraw all routes as per |
1576 |
|
|
* draft-ietf-idr-optional-transitive-00.txt |
1577 |
|
|
* but only if partial bit is set |
1578 |
|
|
*/ |
1579 |
|
|
if ((flags & ATTR_PARTIAL) == 0) |
1580 |
|
|
goto bad_len; |
1581 |
|
|
a->flags |= F_ATTR_PARSE_ERR; |
1582 |
|
|
log_peer_warnx(&peer->conf, "bad EXT_COMMUNITIES, " |
1583 |
|
|
"path invalidated and prefix withdrawn"); |
1584 |
|
|
} |
1585 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, |
1586 |
|
|
ATTR_PARTIAL)) |
1587 |
|
|
goto bad_flags; |
1588 |
|
|
goto optattr; |
1589 |
|
|
case ATTR_ORIGINATOR_ID: |
1590 |
|
|
if (attr_len != 4) |
1591 |
|
|
goto bad_len; |
1592 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) |
1593 |
|
|
goto bad_flags; |
1594 |
|
|
goto optattr; |
1595 |
|
|
case ATTR_CLUSTER_LIST: |
1596 |
|
|
if (attr_len % 4 != 0) |
1597 |
|
|
goto bad_len; |
1598 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) |
1599 |
|
|
goto bad_flags; |
1600 |
|
|
goto optattr; |
1601 |
|
|
case ATTR_MP_REACH_NLRI: |
1602 |
|
|
if (attr_len < 4) |
1603 |
|
|
goto bad_len; |
1604 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) |
1605 |
|
|
goto bad_flags; |
1606 |
|
|
/* the validity is checked in rde_update_dispatch() */ |
1607 |
|
|
if (a->flags & F_ATTR_MP_REACH) |
1608 |
|
|
goto bad_list; |
1609 |
|
|
a->flags |= F_ATTR_MP_REACH; |
1610 |
|
|
|
1611 |
|
|
mpa->reach = p; |
1612 |
|
|
mpa->reach_len = attr_len; |
1613 |
|
|
plen += attr_len; |
1614 |
|
|
break; |
1615 |
|
|
case ATTR_MP_UNREACH_NLRI: |
1616 |
|
|
if (attr_len < 3) |
1617 |
|
|
goto bad_len; |
1618 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL, 0)) |
1619 |
|
|
goto bad_flags; |
1620 |
|
|
/* the validity is checked in rde_update_dispatch() */ |
1621 |
|
|
if (a->flags & F_ATTR_MP_UNREACH) |
1622 |
|
|
goto bad_list; |
1623 |
|
|
a->flags |= F_ATTR_MP_UNREACH; |
1624 |
|
|
|
1625 |
|
|
mpa->unreach = p; |
1626 |
|
|
mpa->unreach_len = attr_len; |
1627 |
|
|
plen += attr_len; |
1628 |
|
|
break; |
1629 |
|
|
case ATTR_AS4_AGGREGATOR: |
1630 |
|
|
if (attr_len != 8) { |
1631 |
|
|
/* see ATTR_AGGREGATOR ... */ |
1632 |
|
|
if ((flags & ATTR_PARTIAL) == 0) |
1633 |
|
|
goto bad_len; |
1634 |
|
|
log_peer_warnx(&peer->conf, "bad AS4_AGGREGATOR, " |
1635 |
|
|
"partial attribute ignored"); |
1636 |
|
|
plen += attr_len; |
1637 |
|
|
break; |
1638 |
|
|
} |
1639 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, |
1640 |
|
|
ATTR_PARTIAL)) |
1641 |
|
|
goto bad_flags; |
1642 |
|
|
a->flags |= F_ATTR_AS4BYTE_NEW; |
1643 |
|
|
goto optattr; |
1644 |
|
|
case ATTR_AS4_PATH: |
1645 |
|
|
if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, |
1646 |
|
|
ATTR_PARTIAL)) |
1647 |
|
|
goto bad_flags; |
1648 |
|
|
if ((error = aspath_verify(p, attr_len, 1)) != 0) { |
1649 |
|
|
/* |
1650 |
|
|
* XXX RFC does not specify how to handle errors. |
1651 |
|
|
* XXX Instead of dropping the session because of a |
1652 |
|
|
* XXX bad path just mark the full update as having |
1653 |
|
|
* XXX a parse error which makes the update no longer |
1654 |
|
|
* XXX eligible and will not be considered for routing |
1655 |
|
|
* XXX or redistribution. |
1656 |
|
|
* XXX We follow draft-ietf-idr-optional-transitive |
1657 |
|
|
* XXX by looking at the partial bit. |
1658 |
|
|
* XXX Consider soft errors similar to a partial attr. |
1659 |
|
|
*/ |
1660 |
|
|
if (flags & ATTR_PARTIAL || error == AS_ERR_SOFT) { |
1661 |
|
|
a->flags |= F_ATTR_PARSE_ERR; |
1662 |
|
|
log_peer_warnx(&peer->conf, "bad AS4_PATH, " |
1663 |
|
|
"path invalidated and prefix withdrawn"); |
1664 |
|
|
goto optattr; |
1665 |
|
|
} else { |
1666 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ASPATH, |
1667 |
|
|
NULL, 0); |
1668 |
|
|
return (-1); |
1669 |
|
|
} |
1670 |
|
|
} |
1671 |
|
|
a->flags |= F_ATTR_AS4BYTE_NEW; |
1672 |
|
|
goto optattr; |
1673 |
|
|
default: |
1674 |
|
|
if ((flags & ATTR_OPTIONAL) == 0) { |
1675 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_UNKNWN_WK_ATTR, |
1676 |
|
|
op, len); |
1677 |
|
|
return (-1); |
1678 |
|
|
} |
1679 |
|
|
optattr: |
1680 |
|
|
if (attr_optadd(a, flags, type, p, attr_len) == -1) { |
1681 |
|
|
bad_list: |
1682 |
|
|
rde_update_err(peer, ERR_UPDATE, ERR_UPD_ATTRLIST, |
1683 |
|
|
NULL, 0); |
1684 |
|
|
return (-1); |
1685 |
|
|
} |
1686 |
|
|
|
1687 |
|
|
plen += attr_len; |
1688 |
|
|
break; |
1689 |
|
|
} |
1690 |
|
|
|
1691 |
|
|
return (plen); |
1692 |
|
|
} |
1693 |
|
|
|
1694 |
|
|
int |
1695 |
|
|
rde_attr_add(struct rde_aspath *a, u_char *p, u_int16_t len) |
1696 |
|
|
{ |
1697 |
|
|
u_int16_t attr_len; |
1698 |
|
|
u_int16_t plen = 0; |
1699 |
|
|
u_int8_t flags; |
1700 |
|
|
u_int8_t type; |
1701 |
|
|
u_int8_t tmp8; |
1702 |
|
|
|
1703 |
|
|
if (a == NULL) /* no aspath, nothing to do */ |
1704 |
|
|
return (0); |
1705 |
|
|
if (len < 3) |
1706 |
|
|
return (-1); |
1707 |
|
|
|
1708 |
|
|
UPD_READ(&flags, p, plen, 1); |
1709 |
|
|
UPD_READ(&type, p, plen, 1); |
1710 |
|
|
|
1711 |
|
|
if (flags & ATTR_EXTLEN) { |
1712 |
|
|
if (len - plen < 2) |
1713 |
|
|
return (-1); |
1714 |
|
|
UPD_READ(&attr_len, p, plen, 2); |
1715 |
|
|
attr_len = ntohs(attr_len); |
1716 |
|
|
} else { |
1717 |
|
|
UPD_READ(&tmp8, p, plen, 1); |
1718 |
|
|
attr_len = tmp8; |
1719 |
|
|
} |
1720 |
|
|
|
1721 |
|
|
if (len - plen < attr_len) |
1722 |
|
|
return (-1); |
1723 |
|
|
|
1724 |
|
|
if (attr_optadd(a, flags, type, p, attr_len) == -1) |
1725 |
|
|
return (-1); |
1726 |
|
|
return (0); |
1727 |
|
|
} |
1728 |
|
|
|
1729 |
|
|
#undef UPD_READ |
1730 |
|
|
#undef CHECK_FLAGS |
1731 |
|
|
|
1732 |
|
|
u_int8_t |
1733 |
|
|
rde_attr_missing(struct rde_aspath *a, int ebgp, u_int16_t nlrilen) |
1734 |
|
|
{ |
1735 |
|
|
/* ATTR_MP_UNREACH_NLRI may be sent alone */ |
1736 |
|
|
if (nlrilen == 0 && a->flags & F_ATTR_MP_UNREACH && |
1737 |
|
|
(a->flags & F_ATTR_MP_REACH) == 0) |
1738 |
|
|
return (0); |
1739 |
|
|
|
1740 |
|
|
if ((a->flags & F_ATTR_ORIGIN) == 0) |
1741 |
|
|
return (ATTR_ORIGIN); |
1742 |
|
|
if ((a->flags & F_ATTR_ASPATH) == 0) |
1743 |
|
|
return (ATTR_ASPATH); |
1744 |
|
|
if ((a->flags & F_ATTR_MP_REACH) == 0 && |
1745 |
|
|
(a->flags & F_ATTR_NEXTHOP) == 0) |
1746 |
|
|
return (ATTR_NEXTHOP); |
1747 |
|
|
if (!ebgp) |
1748 |
|
|
if ((a->flags & F_ATTR_LOCALPREF) == 0) |
1749 |
|
|
return (ATTR_LOCALPREF); |
1750 |
|
|
return (0); |
1751 |
|
|
} |
1752 |
|
|
|
1753 |
|
|
int |
1754 |
|
|
rde_get_mp_nexthop(u_char *data, u_int16_t len, u_int8_t aid, |
1755 |
|
|
struct rde_aspath *asp) |
1756 |
|
|
{ |
1757 |
|
|
struct bgpd_addr nexthop; |
1758 |
|
|
u_int8_t totlen, nhlen; |
1759 |
|
|
|
1760 |
|
|
if (len == 0) |
1761 |
|
|
return (-1); |
1762 |
|
|
|
1763 |
|
|
nhlen = *data++; |
1764 |
|
|
totlen = 1; |
1765 |
|
|
len--; |
1766 |
|
|
|
1767 |
|
|
if (nhlen > len) |
1768 |
|
|
return (-1); |
1769 |
|
|
|
1770 |
|
|
bzero(&nexthop, sizeof(nexthop)); |
1771 |
|
|
nexthop.aid = aid; |
1772 |
|
|
switch (aid) { |
1773 |
|
|
case AID_INET6: |
1774 |
|
|
/* |
1775 |
|
|
* RFC2545 describes that there may be a link-local |
1776 |
|
|
* address carried in nexthop. Yikes! |
1777 |
|
|
* This is not only silly, it is wrong and we just ignore |
1778 |
|
|
* this link-local nexthop. The bgpd session doesn't run |
1779 |
|
|
* over the link-local address so why should all other |
1780 |
|
|
* traffic. |
1781 |
|
|
*/ |
1782 |
|
|
if (nhlen != 16 && nhlen != 32) { |
1783 |
|
|
log_warnx("bad multiprotocol nexthop, bad size"); |
1784 |
|
|
return (-1); |
1785 |
|
|
} |
1786 |
|
|
memcpy(&nexthop.v6.s6_addr, data, 16); |
1787 |
|
|
break; |
1788 |
|
|
case AID_VPN_IPv4: |
1789 |
|
|
/* |
1790 |
|
|
* Neither RFC4364 nor RFC3107 specify the format of the |
1791 |
|
|
* nexthop in an explicit way. The quality of RFC went down |
1792 |
|
|
* the toilet the larger the number got. |
1793 |
|
|
* RFC4364 is very confusing about VPN-IPv4 address and the |
1794 |
|
|
* VPN-IPv4 prefix that carries also a MPLS label. |
1795 |
|
|
* So the nexthop is a 12-byte address with a 64bit RD and |
1796 |
|
|
* an IPv4 address following. In the nexthop case the RD can |
1797 |
|
|
* be ignored. |
1798 |
|
|
* Since the nexthop has to be in the main IPv4 table just |
1799 |
|
|
* create an AID_INET nexthop. So we don't need to handle |
1800 |
|
|
* AID_VPN_IPv4 in nexthop and kroute. |
1801 |
|
|
*/ |
1802 |
|
|
if (nhlen != 12) { |
1803 |
|
|
log_warnx("bad multiprotocol nexthop, bad size"); |
1804 |
|
|
return (-1); |
1805 |
|
|
} |
1806 |
|
|
data += sizeof(u_int64_t); |
1807 |
|
|
nexthop.aid = AID_INET; |
1808 |
|
|
memcpy(&nexthop.v4, data, sizeof(nexthop.v4)); |
1809 |
|
|
break; |
1810 |
|
|
default: |
1811 |
|
|
log_warnx("bad multiprotocol nexthop, bad AID"); |
1812 |
|
|
return (-1); |
1813 |
|
|
} |
1814 |
|
|
|
1815 |
|
|
asp->nexthop = nexthop_get(&nexthop); |
1816 |
|
|
/* |
1817 |
|
|
* lock the nexthop because it is not yet linked else |
1818 |
|
|
* withdraws may remove this nexthop which in turn would |
1819 |
|
|
* cause a use after free error. |
1820 |
|
|
*/ |
1821 |
|
|
asp->nexthop->refcnt++; |
1822 |
|
|
|
1823 |
|
|
/* ignore reserved (old SNPA) field as per RFC4760 */ |
1824 |
|
|
totlen += nhlen + 1; |
1825 |
|
|
data += nhlen + 1; |
1826 |
|
|
|
1827 |
|
|
return (totlen); |
1828 |
|
|
} |
1829 |
|
|
|
1830 |
|
|
int |
1831 |
|
|
rde_update_extract_prefix(u_char *p, u_int16_t len, void *va, |
1832 |
|
|
u_int8_t pfxlen, u_int8_t max) |
1833 |
|
|
{ |
1834 |
|
|
static u_char addrmask[] = { |
1835 |
|
|
0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; |
1836 |
|
|
u_char *a = va; |
1837 |
|
|
int i; |
1838 |
|
|
u_int16_t plen = 0; |
1839 |
|
|
|
1840 |
|
|
for (i = 0; pfxlen && i < max; i++) { |
1841 |
|
|
if (len <= plen) |
1842 |
|
|
return (-1); |
1843 |
|
|
if (pfxlen < 8) { |
1844 |
|
|
a[i] = *p++ & addrmask[pfxlen]; |
1845 |
|
|
plen++; |
1846 |
|
|
break; |
1847 |
|
|
} else { |
1848 |
|
|
a[i] = *p++; |
1849 |
|
|
plen++; |
1850 |
|
|
pfxlen -= 8; |
1851 |
|
|
} |
1852 |
|
|
} |
1853 |
|
|
return (plen); |
1854 |
|
|
} |
1855 |
|
|
|
1856 |
|
|
int |
1857 |
|
|
rde_update_get_prefix(u_char *p, u_int16_t len, struct bgpd_addr *prefix, |
1858 |
|
|
u_int8_t *prefixlen) |
1859 |
|
|
{ |
1860 |
|
|
u_int8_t pfxlen; |
1861 |
|
|
int plen; |
1862 |
|
|
|
1863 |
|
|
if (len < 1) |
1864 |
|
|
return (-1); |
1865 |
|
|
|
1866 |
|
|
pfxlen = *p++; |
1867 |
|
|
len--; |
1868 |
|
|
|
1869 |
|
|
bzero(prefix, sizeof(struct bgpd_addr)); |
1870 |
|
|
prefix->aid = AID_INET; |
1871 |
|
|
*prefixlen = pfxlen; |
1872 |
|
|
|
1873 |
|
|
if ((plen = rde_update_extract_prefix(p, len, &prefix->v4, pfxlen, |
1874 |
|
|
sizeof(prefix->v4))) == -1) |
1875 |
|
|
return (-1); |
1876 |
|
|
|
1877 |
|
|
return (plen + 1); /* pfxlen needs to be added */ |
1878 |
|
|
} |
1879 |
|
|
|
1880 |
|
|
int |
1881 |
|
|
rde_update_get_prefix6(u_char *p, u_int16_t len, struct bgpd_addr *prefix, |
1882 |
|
|
u_int8_t *prefixlen) |
1883 |
|
|
{ |
1884 |
|
|
int plen; |
1885 |
|
|
u_int8_t pfxlen; |
1886 |
|
|
|
1887 |
|
|
if (len < 1) |
1888 |
|
|
return (-1); |
1889 |
|
|
|
1890 |
|
|
pfxlen = *p++; |
1891 |
|
|
len--; |
1892 |
|
|
|
1893 |
|
|
bzero(prefix, sizeof(struct bgpd_addr)); |
1894 |
|
|
prefix->aid = AID_INET6; |
1895 |
|
|
*prefixlen = pfxlen; |
1896 |
|
|
|
1897 |
|
|
if ((plen = rde_update_extract_prefix(p, len, &prefix->v6, pfxlen, |
1898 |
|
|
sizeof(prefix->v6))) == -1) |
1899 |
|
|
return (-1); |
1900 |
|
|
|
1901 |
|
|
return (plen + 1); /* pfxlen needs to be added */ |
1902 |
|
|
} |
1903 |
|
|
|
1904 |
|
|
int |
1905 |
|
|
rde_update_get_vpn4(u_char *p, u_int16_t len, struct bgpd_addr *prefix, |
1906 |
|
|
u_int8_t *prefixlen) |
1907 |
|
|
{ |
1908 |
|
|
int rv, done = 0; |
1909 |
|
|
u_int8_t pfxlen; |
1910 |
|
|
u_int16_t plen; |
1911 |
|
|
|
1912 |
|
|
if (len < 1) |
1913 |
|
|
return (-1); |
1914 |
|
|
|
1915 |
|
|
memcpy(&pfxlen, p, 1); |
1916 |
|
|
p += 1; |
1917 |
|
|
plen = 1; |
1918 |
|
|
|
1919 |
|
|
bzero(prefix, sizeof(struct bgpd_addr)); |
1920 |
|
|
|
1921 |
|
|
/* label stack */ |
1922 |
|
|
do { |
1923 |
|
|
if (len - plen < 3 || pfxlen < 3 * 8) |
1924 |
|
|
return (-1); |
1925 |
|
|
if (prefix->vpn4.labellen + 3U > |
1926 |
|
|
sizeof(prefix->vpn4.labelstack)) |
1927 |
|
|
return (-1); |
1928 |
|
|
prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++; |
1929 |
|
|
prefix->vpn4.labelstack[prefix->vpn4.labellen++] = *p++; |
1930 |
|
|
prefix->vpn4.labelstack[prefix->vpn4.labellen] = *p++; |
1931 |
|
|
if (prefix->vpn4.labelstack[prefix->vpn4.labellen] & |
1932 |
|
|
BGP_MPLS_BOS) |
1933 |
|
|
done = 1; |
1934 |
|
|
prefix->vpn4.labellen++; |
1935 |
|
|
plen += 3; |
1936 |
|
|
pfxlen -= 3 * 8; |
1937 |
|
|
} while (!done); |
1938 |
|
|
|
1939 |
|
|
/* RD */ |
1940 |
|
|
if (len - plen < (int)sizeof(u_int64_t) || |
1941 |
|
|
pfxlen < sizeof(u_int64_t) * 8) |
1942 |
|
|
return (-1); |
1943 |
|
|
memcpy(&prefix->vpn4.rd, p, sizeof(u_int64_t)); |
1944 |
|
|
pfxlen -= sizeof(u_int64_t) * 8; |
1945 |
|
|
p += sizeof(u_int64_t); |
1946 |
|
|
plen += sizeof(u_int64_t); |
1947 |
|
|
|
1948 |
|
|
/* prefix */ |
1949 |
|
|
prefix->aid = AID_VPN_IPv4; |
1950 |
|
|
*prefixlen = pfxlen; |
1951 |
|
|
|
1952 |
|
|
if ((rv = rde_update_extract_prefix(p, len, &prefix->vpn4.addr, |
1953 |
|
|
pfxlen, sizeof(prefix->vpn4.addr))) == -1) |
1954 |
|
|
return (-1); |
1955 |
|
|
|
1956 |
|
|
return (plen + rv); |
1957 |
|
|
} |
1958 |
|
|
|
1959 |
|
|
void |
1960 |
|
|
rde_update_err(struct rde_peer *peer, u_int8_t error, u_int8_t suberr, |
1961 |
|
|
void *data, u_int16_t size) |
1962 |
|
|
{ |
1963 |
|
|
struct ibuf *wbuf; |
1964 |
|
|
|
1965 |
|
|
if ((wbuf = imsg_create(ibuf_se, IMSG_UPDATE_ERR, peer->conf.id, 0, |
1966 |
|
|
size + sizeof(error) + sizeof(suberr))) == NULL) |
1967 |
|
|
fatal("%s %d imsg_create error", __func__, __LINE__); |
1968 |
|
|
if (imsg_add(wbuf, &error, sizeof(error)) == -1 || |
1969 |
|
|
imsg_add(wbuf, &suberr, sizeof(suberr)) == -1 || |
1970 |
|
|
imsg_add(wbuf, data, size) == -1) |
1971 |
|
|
fatal("%s %d imsg_add error", __func__, __LINE__); |
1972 |
|
|
imsg_close(ibuf_se, wbuf); |
1973 |
|
|
peer->state = PEER_ERR; |
1974 |
|
|
} |
1975 |
|
|
|
1976 |
|
|
void |
1977 |
|
|
rde_update_log(const char *message, u_int16_t rid, |
1978 |
|
|
const struct rde_peer *peer, const struct bgpd_addr *next, |
1979 |
|
|
const struct bgpd_addr *prefix, u_int8_t prefixlen) |
1980 |
|
|
{ |
1981 |
|
|
char *l = NULL; |
1982 |
|
|
char *n = NULL; |
1983 |
|
|
char *p = NULL; |
1984 |
|
|
|
1985 |
|
|
if ( !((conf->log & BGPD_LOG_UPDATES) || |
1986 |
|
|
(peer->conf.flags & PEERFLAG_LOG_UPDATES)) ) |
1987 |
|
|
return; |
1988 |
|
|
|
1989 |
|
|
if (next != NULL) |
1990 |
|
|
if (asprintf(&n, " via %s", log_addr(next)) == -1) |
1991 |
|
|
n = NULL; |
1992 |
|
|
if (asprintf(&p, "%s/%u", log_addr(prefix), prefixlen) == -1) |
1993 |
|
|
p = NULL; |
1994 |
|
|
l = log_fmt_peer(&peer->conf); |
1995 |
|
|
log_info("Rib %s: %s AS%s: %s %s%s", ribs[rid].name, |
1996 |
|
|
l, log_as(peer->conf.remote_as), message, |
1997 |
|
|
p ? p : "out of memory", n ? n : ""); |
1998 |
|
|
|
1999 |
|
|
free(l); |
2000 |
|
|
free(n); |
2001 |
|
|
free(p); |
2002 |
|
|
} |
2003 |
|
|
|
2004 |
|
|
/* |
2005 |
|
|
* 4-Byte ASN helper function. |
2006 |
|
|
* Two scenarios need to be considered: |
2007 |
|
|
* - NEW session with NEW attributes present -> just remove the attributes |
2008 |
|
|
* - OLD session with NEW attributes present -> try to merge them |
2009 |
|
|
*/ |
2010 |
|
|
void |
2011 |
|
|
rde_as4byte_fixup(struct rde_peer *peer, struct rde_aspath *a) |
2012 |
|
|
{ |
2013 |
|
|
struct attr *nasp, *naggr, *oaggr; |
2014 |
|
|
u_int32_t as; |
2015 |
|
|
|
2016 |
|
|
/* |
2017 |
|
|
* if either ATTR_AS4_AGGREGATOR or ATTR_AS4_PATH is present |
2018 |
|
|
* try to fixup the attributes. |
2019 |
|
|
* Do not fixup if F_ATTR_PARSE_ERR is set. |
2020 |
|
|
*/ |
2021 |
|
|
if (!(a->flags & F_ATTR_AS4BYTE_NEW) || a->flags & F_ATTR_PARSE_ERR) |
2022 |
|
|
return; |
2023 |
|
|
|
2024 |
|
|
/* first get the attributes */ |
2025 |
|
|
nasp = attr_optget(a, ATTR_AS4_PATH); |
2026 |
|
|
naggr = attr_optget(a, ATTR_AS4_AGGREGATOR); |
2027 |
|
|
|
2028 |
|
|
if (rde_as4byte(peer)) { |
2029 |
|
|
/* NEW session using 4-byte ASNs */ |
2030 |
|
|
if (nasp) { |
2031 |
|
|
log_peer_warnx(&peer->conf, "uses 4-byte ASN " |
2032 |
|
|
"but sent AS4_PATH attribute."); |
2033 |
|
|
attr_free(a, nasp); |
2034 |
|
|
} |
2035 |
|
|
if (naggr) { |
2036 |
|
|
log_peer_warnx(&peer->conf, "uses 4-byte ASN " |
2037 |
|
|
"but sent AS4_AGGREGATOR attribute."); |
2038 |
|
|
attr_free(a, naggr); |
2039 |
|
|
} |
2040 |
|
|
return; |
2041 |
|
|
} |
2042 |
|
|
/* OLD session using 2-byte ASNs */ |
2043 |
|
|
/* try to merge the new attributes into the old ones */ |
2044 |
|
|
if ((oaggr = attr_optget(a, ATTR_AGGREGATOR))) { |
2045 |
|
|
memcpy(&as, oaggr->data, sizeof(as)); |
2046 |
|
|
if (ntohl(as) != AS_TRANS) { |
2047 |
|
|
/* per RFC ignore AS4_PATH and AS4_AGGREGATOR */ |
2048 |
|
|
if (nasp) |
2049 |
|
|
attr_free(a, nasp); |
2050 |
|
|
if (naggr) |
2051 |
|
|
attr_free(a, naggr); |
2052 |
|
|
return; |
2053 |
|
|
} |
2054 |
|
|
if (naggr) { |
2055 |
|
|
/* switch over to new AGGREGATOR */ |
2056 |
|
|
attr_free(a, oaggr); |
2057 |
|
|
if (attr_optadd(a, ATTR_OPTIONAL | ATTR_TRANSITIVE, |
2058 |
|
|
ATTR_AGGREGATOR, naggr->data, naggr->len)) |
2059 |
|
|
fatalx("attr_optadd failed but impossible"); |
2060 |
|
|
} |
2061 |
|
|
} |
2062 |
|
|
/* there is no need for AS4_AGGREGATOR any more */ |
2063 |
|
|
if (naggr) |
2064 |
|
|
attr_free(a, naggr); |
2065 |
|
|
|
2066 |
|
|
/* merge AS4_PATH with ASPATH */ |
2067 |
|
|
if (nasp) |
2068 |
|
|
aspath_merge(a, nasp); |
2069 |
|
|
} |
2070 |
|
|
|
2071 |
|
|
|
2072 |
|
|
/* |
2073 |
|
|
* route reflector helper function |
2074 |
|
|
*/ |
2075 |
|
|
void |
2076 |
|
|
rde_reflector(struct rde_peer *peer, struct rde_aspath *asp) |
2077 |
|
|
{ |
2078 |
|
|
struct attr *a; |
2079 |
|
|
u_int8_t *p; |
2080 |
|
|
u_int16_t len; |
2081 |
|
|
u_int32_t id; |
2082 |
|
|
|
2083 |
|
|
/* do not consider updates with parse errors */ |
2084 |
|
|
if (asp->flags & F_ATTR_PARSE_ERR) |
2085 |
|
|
return; |
2086 |
|
|
|
2087 |
|
|
/* check for originator id if eq router_id drop */ |
2088 |
|
|
if ((a = attr_optget(asp, ATTR_ORIGINATOR_ID)) != NULL) { |
2089 |
|
|
if (memcmp(&conf->bgpid, a->data, sizeof(conf->bgpid)) == 0) { |
2090 |
|
|
/* this is coming from myself */ |
2091 |
|
|
asp->flags |= F_ATTR_LOOP; |
2092 |
|
|
return; |
2093 |
|
|
} |
2094 |
|
|
} else if (conf->flags & BGPD_FLAG_REFLECTOR) { |
2095 |
|
|
if (peer->conf.ebgp) |
2096 |
|
|
id = conf->bgpid; |
2097 |
|
|
else |
2098 |
|
|
id = htonl(peer->remote_bgpid); |
2099 |
|
|
if (attr_optadd(asp, ATTR_OPTIONAL, ATTR_ORIGINATOR_ID, |
2100 |
|
|
&id, sizeof(u_int32_t)) == -1) |
2101 |
|
|
fatalx("attr_optadd failed but impossible"); |
2102 |
|
|
} |
2103 |
|
|
|
2104 |
|
|
/* check for own id in the cluster list */ |
2105 |
|
|
if (conf->flags & BGPD_FLAG_REFLECTOR) { |
2106 |
|
|
if ((a = attr_optget(asp, ATTR_CLUSTER_LIST)) != NULL) { |
2107 |
|
|
for (len = 0; len < a->len; |
2108 |
|
|
len += sizeof(conf->clusterid)) |
2109 |
|
|
/* check if coming from my cluster */ |
2110 |
|
|
if (memcmp(&conf->clusterid, a->data + len, |
2111 |
|
|
sizeof(conf->clusterid)) == 0) { |
2112 |
|
|
asp->flags |= F_ATTR_LOOP; |
2113 |
|
|
return; |
2114 |
|
|
} |
2115 |
|
|
|
2116 |
|
|
/* prepend own clusterid by replacing attribute */ |
2117 |
|
|
len = a->len + sizeof(conf->clusterid); |
2118 |
|
|
if (len < a->len) |
2119 |
|
|
fatalx("rde_reflector: cluster-list overflow"); |
2120 |
|
|
if ((p = malloc(len)) == NULL) |
2121 |
|
|
fatal("rde_reflector"); |
2122 |
|
|
memcpy(p, &conf->clusterid, sizeof(conf->clusterid)); |
2123 |
|
|
memcpy(p + sizeof(conf->clusterid), a->data, a->len); |
2124 |
|
|
attr_free(asp, a); |
2125 |
|
|
if (attr_optadd(asp, ATTR_OPTIONAL, ATTR_CLUSTER_LIST, |
2126 |
|
|
p, len) == -1) |
2127 |
|
|
fatalx("attr_optadd failed but impossible"); |
2128 |
|
|
free(p); |
2129 |
|
|
} else if (attr_optadd(asp, ATTR_OPTIONAL, ATTR_CLUSTER_LIST, |
2130 |
|
|
&conf->clusterid, sizeof(conf->clusterid)) == -1) |
2131 |
|
|
fatalx("attr_optadd failed but impossible"); |
2132 |
|
|
} |
2133 |
|
|
} |
2134 |
|
|
|
2135 |
|
|
/* |
2136 |
|
|
* control specific functions |
2137 |
|
|
*/ |
2138 |
|
|
void |
2139 |
|
|
rde_dump_rib_as(struct prefix *p, struct rde_aspath *asp, pid_t pid, int flags) |
2140 |
|
|
{ |
2141 |
|
|
struct ctl_show_rib rib; |
2142 |
|
|
struct ibuf *wbuf; |
2143 |
|
|
struct attr *a; |
2144 |
|
|
void *bp; |
2145 |
|
|
time_t staletime; |
2146 |
|
|
u_int8_t l; |
2147 |
|
|
|
2148 |
|
|
bzero(&rib, sizeof(rib)); |
2149 |
|
|
rib.lastchange = p->lastchange; |
2150 |
|
|
rib.local_pref = asp->lpref; |
2151 |
|
|
rib.med = asp->med; |
2152 |
|
|
rib.weight = asp->weight; |
2153 |
|
|
strlcpy(rib.descr, asp->peer->conf.descr, sizeof(rib.descr)); |
2154 |
|
|
memcpy(&rib.remote_addr, &asp->peer->remote_addr, |
2155 |
|
|
sizeof(rib.remote_addr)); |
2156 |
|
|
rib.remote_id = asp->peer->remote_bgpid; |
2157 |
|
|
if (asp->nexthop != NULL) { |
2158 |
|
|
memcpy(&rib.true_nexthop, &asp->nexthop->true_nexthop, |
2159 |
|
|
sizeof(rib.true_nexthop)); |
2160 |
|
|
memcpy(&rib.exit_nexthop, &asp->nexthop->exit_nexthop, |
2161 |
|
|
sizeof(rib.exit_nexthop)); |
2162 |
|
|
} else { |
2163 |
|
|
/* announced network may have a NULL nexthop */ |
2164 |
|
|
bzero(&rib.true_nexthop, sizeof(rib.true_nexthop)); |
2165 |
|
|
bzero(&rib.exit_nexthop, sizeof(rib.exit_nexthop)); |
2166 |
|
|
rib.true_nexthop.aid = p->prefix->aid; |
2167 |
|
|
rib.exit_nexthop.aid = p->prefix->aid; |
2168 |
|
|
} |
2169 |
|
|
pt_getaddr(p->prefix, &rib.prefix); |
2170 |
|
|
rib.prefixlen = p->prefix->prefixlen; |
2171 |
|
|
rib.origin = asp->origin; |
2172 |
|
|
rib.flags = 0; |
2173 |
|
|
if (p->rib->active == p) |
2174 |
|
|
rib.flags |= F_PREF_ACTIVE; |
2175 |
|
|
if (!asp->peer->conf.ebgp) |
2176 |
|
|
rib.flags |= F_PREF_INTERNAL; |
2177 |
|
|
if (asp->flags & F_PREFIX_ANNOUNCED) |
2178 |
|
|
rib.flags |= F_PREF_ANNOUNCE; |
2179 |
|
|
if (asp->nexthop == NULL || asp->nexthop->state == NEXTHOP_REACH) |
2180 |
|
|
rib.flags |= F_PREF_ELIGIBLE; |
2181 |
|
|
if (asp->flags & F_ATTR_LOOP) |
2182 |
|
|
rib.flags &= ~F_PREF_ELIGIBLE; |
2183 |
|
|
staletime = asp->peer->staletime[p->prefix->aid]; |
2184 |
|
|
if (staletime && p->lastchange <= staletime) |
2185 |
|
|
rib.flags |= F_PREF_STALE; |
2186 |
|
|
rib.aspath_len = aspath_length(asp->aspath); |
2187 |
|
|
|
2188 |
|
|
if ((wbuf = imsg_create(ibuf_se_ctl, IMSG_CTL_SHOW_RIB, 0, pid, |
2189 |
|
|
sizeof(rib) + rib.aspath_len)) == NULL) |
2190 |
|
|
return; |
2191 |
|
|
if (imsg_add(wbuf, &rib, sizeof(rib)) == -1 || |
2192 |
|
|
imsg_add(wbuf, aspath_dump(asp->aspath), |
2193 |
|
|
rib.aspath_len) == -1) |
2194 |
|
|
return; |
2195 |
|
|
imsg_close(ibuf_se_ctl, wbuf); |
2196 |
|
|
|
2197 |
|
|
if (flags & F_CTL_DETAIL) |
2198 |
|
|
for (l = 0; l < asp->others_len; l++) { |
2199 |
|
|
if ((a = asp->others[l]) == NULL) |
2200 |
|
|
break; |
2201 |
|
|
if ((wbuf = imsg_create(ibuf_se_ctl, |
2202 |
|
|
IMSG_CTL_SHOW_RIB_ATTR, 0, pid, |
2203 |
|
|
attr_optlen(a))) == NULL) |
2204 |
|
|
return; |
2205 |
|
|
if ((bp = ibuf_reserve(wbuf, attr_optlen(a))) == NULL) { |
2206 |
|
|
ibuf_free(wbuf); |
2207 |
|
|
return; |
2208 |
|
|
} |
2209 |
|
|
if (attr_write(bp, attr_optlen(a), a->flags, |
2210 |
|
|
a->type, a->data, a->len) == -1) { |
2211 |
|
|
ibuf_free(wbuf); |
2212 |
|
|
return; |
2213 |
|
|
} |
2214 |
|
|
imsg_close(ibuf_se_ctl, wbuf); |
2215 |
|
|
} |
2216 |
|
|
} |
2217 |
|
|
|
2218 |
|
|
void |
2219 |
|
|
rde_dump_filterout(struct rde_peer *peer, struct prefix *p, |
2220 |
|
|
struct ctl_show_rib_request *req) |
2221 |
|
|
{ |
2222 |
|
|
struct bgpd_addr addr; |
2223 |
|
|
struct rde_aspath *asp; |
2224 |
|
|
enum filter_actions a; |
2225 |
|
|
|
2226 |
|
|
if (up_test_update(peer, p) != 1) |
2227 |
|
|
return; |
2228 |
|
|
|
2229 |
|
|
pt_getaddr(p->prefix, &addr); |
2230 |
|
|
a = rde_filter(out_rules, &asp, peer, p->aspath, &addr, |
2231 |
|
|
p->prefix->prefixlen, p->aspath->peer); |
2232 |
|
|
if (asp) |
2233 |
|
|
asp->peer = p->aspath->peer; |
2234 |
|
|
else |
2235 |
|
|
asp = p->aspath; |
2236 |
|
|
|
2237 |
|
|
if (a == ACTION_ALLOW) |
2238 |
|
|
rde_dump_rib_as(p, asp, req->pid, req->flags); |
2239 |
|
|
|
2240 |
|
|
if (asp != p->aspath) |
2241 |
|
|
path_put(asp); |
2242 |
|
|
} |
2243 |
|
|
|
2244 |
|
|
void |
2245 |
|
|
rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) |
2246 |
|
|
{ |
2247 |
|
|
struct rde_peer *peer; |
2248 |
|
|
|
2249 |
|
|
if (req->flags & F_CTL_ADJ_IN || |
2250 |
|
|
!(req->flags & (F_CTL_ADJ_IN|F_CTL_ADJ_OUT))) { |
2251 |
|
|
if (req->peerid && req->peerid != p->aspath->peer->conf.id) |
2252 |
|
|
return; |
2253 |
|
|
if (req->type == IMSG_CTL_SHOW_RIB_AS && |
2254 |
|
|
!aspath_match(p->aspath->aspath->data, |
2255 |
|
|
p->aspath->aspath->len, &req->as, req->as.as)) |
2256 |
|
|
return; |
2257 |
|
|
if (req->type == IMSG_CTL_SHOW_RIB_COMMUNITY && |
2258 |
|
|
!community_match(p->aspath, req->community.as, |
2259 |
|
|
req->community.type)) |
2260 |
|
|
return; |
2261 |
|
|
if ((req->flags & F_CTL_ACTIVE) && p->rib->active != p) |
2262 |
|
|
return; |
2263 |
|
|
rde_dump_rib_as(p, p->aspath, req->pid, req->flags); |
2264 |
|
|
} else if (req->flags & F_CTL_ADJ_OUT) { |
2265 |
|
|
if (p->rib->active != p) |
2266 |
|
|
/* only consider active prefix */ |
2267 |
|
|
return; |
2268 |
|
|
if (req->peerid) { |
2269 |
|
|
if ((peer = peer_get(req->peerid)) != NULL) |
2270 |
|
|
rde_dump_filterout(peer, p, req); |
2271 |
|
|
return; |
2272 |
|
|
} |
2273 |
|
|
} |
2274 |
|
|
} |
2275 |
|
|
|
2276 |
|
|
void |
2277 |
|
|
rde_dump_upcall(struct rib_entry *re, void *ptr) |
2278 |
|
|
{ |
2279 |
|
|
struct prefix *p; |
2280 |
|
|
struct rde_dump_ctx *ctx = ptr; |
2281 |
|
|
|
2282 |
|
|
LIST_FOREACH(p, &re->prefix_h, rib_l) |
2283 |
|
|
rde_dump_filter(p, &ctx->req); |
2284 |
|
|
} |
2285 |
|
|
|
2286 |
|
|
void |
2287 |
|
|
rde_dump_prefix_upcall(struct rib_entry *re, void *ptr) |
2288 |
|
|
{ |
2289 |
|
|
struct rde_dump_ctx *ctx = ptr; |
2290 |
|
|
struct prefix *p; |
2291 |
|
|
struct pt_entry *pt; |
2292 |
|
|
struct bgpd_addr addr; |
2293 |
|
|
|
2294 |
|
|
pt = re->prefix; |
2295 |
|
|
pt_getaddr(pt, &addr); |
2296 |
|
|
if (addr.aid != ctx->req.prefix.aid) |
2297 |
|
|
return; |
2298 |
|
|
if (ctx->req.prefixlen > pt->prefixlen) |
2299 |
|
|
return; |
2300 |
|
|
if (!prefix_compare(&ctx->req.prefix, &addr, ctx->req.prefixlen)) |
2301 |
|
|
LIST_FOREACH(p, &re->prefix_h, rib_l) |
2302 |
|
|
rde_dump_filter(p, &ctx->req); |
2303 |
|
|
} |
2304 |
|
|
|
2305 |
|
|
void |
2306 |
|
|
rde_dump_ctx_new(struct ctl_show_rib_request *req, pid_t pid, |
2307 |
|
|
enum imsg_type type) |
2308 |
|
|
{ |
2309 |
|
|
struct rde_dump_ctx *ctx; |
2310 |
|
|
struct rib_entry *re; |
2311 |
|
|
u_int error; |
2312 |
|
|
u_int16_t id; |
2313 |
|
|
u_int8_t hostplen; |
2314 |
|
|
|
2315 |
|
|
if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { |
2316 |
|
|
log_warn("rde_dump_ctx_new"); |
2317 |
|
|
error = CTL_RES_NOMEM; |
2318 |
|
|
imsg_compose(ibuf_se_ctl, IMSG_CTL_RESULT, 0, pid, -1, &error, |
2319 |
|
|
sizeof(error)); |
2320 |
|
|
return; |
2321 |
|
|
} |
2322 |
|
|
if ((id = rib_find(req->rib)) == RIB_FAILED) { |
2323 |
|
|
log_warnx("rde_dump_ctx_new: no such rib %s", req->rib); |
2324 |
|
|
error = CTL_RES_NOSUCHPEER; |
2325 |
|
|
imsg_compose(ibuf_se_ctl, IMSG_CTL_RESULT, 0, pid, -1, &error, |
2326 |
|
|
sizeof(error)); |
2327 |
|
|
free(ctx); |
2328 |
|
|
return; |
2329 |
|
|
} |
2330 |
|
|
|
2331 |
|
|
memcpy(&ctx->req, req, sizeof(struct ctl_show_rib_request)); |
2332 |
|
|
ctx->req.pid = pid; |
2333 |
|
|
ctx->req.type = type; |
2334 |
|
|
ctx->ribctx.ctx_count = RDE_RUNNER_ROUNDS; |
2335 |
|
|
ctx->ribctx.ctx_rib = &ribs[id]; |
2336 |
|
|
switch (ctx->req.type) { |
2337 |
|
|
case IMSG_CTL_SHOW_NETWORK: |
2338 |
|
|
ctx->ribctx.ctx_upcall = network_dump_upcall; |
2339 |
|
|
break; |
2340 |
|
|
case IMSG_CTL_SHOW_RIB: |
2341 |
|
|
case IMSG_CTL_SHOW_RIB_AS: |
2342 |
|
|
case IMSG_CTL_SHOW_RIB_COMMUNITY: |
2343 |
|
|
ctx->ribctx.ctx_upcall = rde_dump_upcall; |
2344 |
|
|
break; |
2345 |
|
|
case IMSG_CTL_SHOW_RIB_PREFIX: |
2346 |
|
|
if (req->flags & F_LONGER) { |
2347 |
|
|
ctx->ribctx.ctx_upcall = rde_dump_prefix_upcall; |
2348 |
|
|
break; |
2349 |
|
|
} |
2350 |
|
|
switch (req->prefix.aid) { |
2351 |
|
|
case AID_INET: |
2352 |
|
|
case AID_VPN_IPv4: |
2353 |
|
|
hostplen = 32; |
2354 |
|
|
break; |
2355 |
|
|
case AID_INET6: |
2356 |
|
|
hostplen = 128; |
2357 |
|
|
break; |
2358 |
|
|
default: |
2359 |
|
|
fatalx("rde_dump_ctx_new: unknown af"); |
2360 |
|
|
} |
2361 |
|
|
if (req->prefixlen == hostplen) |
2362 |
|
|
re = rib_lookup(&ribs[id], &req->prefix); |
2363 |
|
|
else |
2364 |
|
|
re = rib_get(&ribs[id], &req->prefix, req->prefixlen); |
2365 |
|
|
if (re) |
2366 |
|
|
rde_dump_upcall(re, ctx); |
2367 |
|
|
rde_dump_done(ctx); |
2368 |
|
|
return; |
2369 |
|
|
default: |
2370 |
|
|
fatalx("rde_dump_ctx_new: unsupported imsg type"); |
2371 |
|
|
} |
2372 |
|
|
ctx->ribctx.ctx_done = rde_dump_done; |
2373 |
|
|
ctx->ribctx.ctx_arg = ctx; |
2374 |
|
|
ctx->ribctx.ctx_aid = ctx->req.aid; |
2375 |
|
|
rib_dump_r(&ctx->ribctx); |
2376 |
|
|
} |
2377 |
|
|
|
2378 |
|
|
void |
2379 |
|
|
rde_dump_done(void *arg) |
2380 |
|
|
{ |
2381 |
|
|
struct rde_dump_ctx *ctx = arg; |
2382 |
|
|
|
2383 |
|
|
imsg_compose(ibuf_se_ctl, IMSG_CTL_END, 0, ctx->req.pid, |
2384 |
|
|
-1, NULL, 0); |
2385 |
|
|
free(ctx); |
2386 |
|
|
} |
2387 |
|
|
|
2388 |
|
|
void |
2389 |
|
|
rde_dump_mrt_new(struct mrt *mrt, pid_t pid, int fd) |
2390 |
|
|
{ |
2391 |
|
|
struct rde_mrt_ctx *ctx; |
2392 |
|
|
u_int16_t id; |
2393 |
|
|
|
2394 |
|
|
if ((ctx = calloc(1, sizeof(*ctx))) == NULL) { |
2395 |
|
|
log_warn("rde_dump_mrt_new"); |
2396 |
|
|
return; |
2397 |
|
|
} |
2398 |
|
|
memcpy(&ctx->mrt, mrt, sizeof(struct mrt)); |
2399 |
|
|
TAILQ_INIT(&ctx->mrt.wbuf.bufs); |
2400 |
|
|
ctx->mrt.wbuf.fd = fd; |
2401 |
|
|
ctx->mrt.state = MRT_STATE_RUNNING; |
2402 |
|
|
id = rib_find(ctx->mrt.rib); |
2403 |
|
|
if (id == RIB_FAILED) { |
2404 |
|
|
log_warnx("non existing RIB %s for mrt dump", ctx->mrt.rib); |
2405 |
|
|
free(ctx); |
2406 |
|
|
return; |
2407 |
|
|
} |
2408 |
|
|
|
2409 |
|
|
if (ctx->mrt.type == MRT_TABLE_DUMP_V2) |
2410 |
|
|
mrt_dump_v2_hdr(&ctx->mrt, conf, &peerlist); |
2411 |
|
|
|
2412 |
|
|
ctx->ribctx.ctx_count = RDE_RUNNER_ROUNDS; |
2413 |
|
|
ctx->ribctx.ctx_rib = &ribs[id]; |
2414 |
|
|
ctx->ribctx.ctx_upcall = mrt_dump_upcall; |
2415 |
|
|
ctx->ribctx.ctx_done = mrt_done; |
2416 |
|
|
ctx->ribctx.ctx_arg = &ctx->mrt; |
2417 |
|
|
ctx->ribctx.ctx_aid = AID_UNSPEC; |
2418 |
|
|
LIST_INSERT_HEAD(&rde_mrts, ctx, entry); |
2419 |
|
|
rde_mrt_cnt++; |
2420 |
|
|
rib_dump_r(&ctx->ribctx); |
2421 |
|
|
} |
2422 |
|
|
|
2423 |
|
|
/* |
2424 |
|
|
* kroute specific functions |
2425 |
|
|
*/ |
2426 |
|
|
int |
2427 |
|
|
rde_rdomain_import(struct rde_aspath *asp, struct rdomain *rd) |
2428 |
|
|
{ |
2429 |
|
|
struct filter_set *s; |
2430 |
|
|
|
2431 |
|
|
TAILQ_FOREACH(s, &rd->import, entry) { |
2432 |
|
|
if (community_ext_match(asp, &s->action.ext_community, 0)) |
2433 |
|
|
return (1); |
2434 |
|
|
} |
2435 |
|
|
return (0); |
2436 |
|
|
} |
2437 |
|
|
|
2438 |
|
|
void |
2439 |
|
|
rde_send_kroute(struct prefix *new, struct prefix *old, u_int16_t ribid) |
2440 |
|
|
{ |
2441 |
|
|
struct kroute_full kr; |
2442 |
|
|
struct bgpd_addr addr; |
2443 |
|
|
struct prefix *p; |
2444 |
|
|
struct rdomain *rd; |
2445 |
|
|
enum imsg_type type; |
2446 |
|
|
|
2447 |
|
|
/* |
2448 |
|
|
* Make sure that self announce prefixes are not committed to the |
2449 |
|
|
* FIB. If both prefixes are unreachable no update is needed. |
2450 |
|
|
*/ |
2451 |
|
|
if ((old == NULL || old->aspath->flags & F_PREFIX_ANNOUNCED) && |
2452 |
|
|
(new == NULL || new->aspath->flags & F_PREFIX_ANNOUNCED)) |
2453 |
|
|
return; |
2454 |
|
|
|
2455 |
|
|
if (new == NULL || new->aspath->flags & F_PREFIX_ANNOUNCED) { |
2456 |
|
|
type = IMSG_KROUTE_DELETE; |
2457 |
|
|
p = old; |
2458 |
|
|
} else { |
2459 |
|
|
type = IMSG_KROUTE_CHANGE; |
2460 |
|
|
p = new; |
2461 |
|
|
} |
2462 |
|
|
|
2463 |
|
|
pt_getaddr(p->prefix, &addr); |
2464 |
|
|
bzero(&kr, sizeof(kr)); |
2465 |
|
|
memcpy(&kr.prefix, &addr, sizeof(kr.prefix)); |
2466 |
|
|
kr.prefixlen = p->prefix->prefixlen; |
2467 |
|
|
if (p->aspath->flags & F_NEXTHOP_REJECT) |
2468 |
|
|
kr.flags |= F_REJECT; |
2469 |
|
|
if (p->aspath->flags & F_NEXTHOP_BLACKHOLE) |
2470 |
|
|
kr.flags |= F_BLACKHOLE; |
2471 |
|
|
if (type == IMSG_KROUTE_CHANGE) |
2472 |
|
|
memcpy(&kr.nexthop, &p->aspath->nexthop->true_nexthop, |
2473 |
|
|
sizeof(kr.nexthop)); |
2474 |
|
|
strlcpy(kr.label, rtlabel_id2name(p->aspath->rtlabelid), |
2475 |
|
|
sizeof(kr.label)); |
2476 |
|
|
|
2477 |
|
|
switch (addr.aid) { |
2478 |
|
|
case AID_VPN_IPv4: |
2479 |
|
|
if (ribid != 1) |
2480 |
|
|
/* not Loc-RIB, no update for VPNs */ |
2481 |
|
|
break; |
2482 |
|
|
|
2483 |
|
|
SIMPLEQ_FOREACH(rd, rdomains_l, entry) { |
2484 |
|
|
if (!rde_rdomain_import(p->aspath, rd)) |
2485 |
|
|
continue; |
2486 |
|
|
/* must send exit_nexthop so that correct MPLS tunnel |
2487 |
|
|
* is chosen |
2488 |
|
|
*/ |
2489 |
|
|
if (type == IMSG_KROUTE_CHANGE) |
2490 |
|
|
memcpy(&kr.nexthop, |
2491 |
|
|
&p->aspath->nexthop->exit_nexthop, |
2492 |
|
|
sizeof(kr.nexthop)); |
2493 |
|
|
if (imsg_compose(ibuf_main, type, rd->rtableid, 0, -1, |
2494 |
|
|
&kr, sizeof(kr)) == -1) |
2495 |
|
|
fatal("%s %d imsg_compose error", __func__, |
2496 |
|
|
__LINE__); |
2497 |
|
|
} |
2498 |
|
|
break; |
2499 |
|
|
default: |
2500 |
|
|
if (imsg_compose(ibuf_main, type, ribs[ribid].rtableid, 0, -1, |
2501 |
|
|
&kr, sizeof(kr)) == -1) |
2502 |
|
|
fatal("%s %d imsg_compose error", __func__, __LINE__); |
2503 |
|
|
break; |
2504 |
|
|
} |
2505 |
|
|
} |
2506 |
|
|
|
2507 |
|
|
/* |
2508 |
|
|
* pf table specific functions |
2509 |
|
|
*/ |
2510 |
|
|
void |
2511 |
|
|
rde_send_pftable(u_int16_t id, struct bgpd_addr *addr, |
2512 |
|
|
u_int8_t len, int del) |
2513 |
|
|
{ |
2514 |
|
|
struct pftable_msg pfm; |
2515 |
|
|
|
2516 |
|
|
if (id == 0) |
2517 |
|
|
return; |
2518 |
|
|
|
2519 |
|
|
/* do not run while cleaning up */ |
2520 |
|
|
if (rde_quit) |
2521 |
|
|
return; |
2522 |
|
|
|
2523 |
|
|
bzero(&pfm, sizeof(pfm)); |
2524 |
|
|
strlcpy(pfm.pftable, pftable_id2name(id), sizeof(pfm.pftable)); |
2525 |
|
|
memcpy(&pfm.addr, addr, sizeof(pfm.addr)); |
2526 |
|
|
pfm.len = len; |
2527 |
|
|
|
2528 |
|
|
if (imsg_compose(ibuf_main, |
2529 |
|
|
del ? IMSG_PFTABLE_REMOVE : IMSG_PFTABLE_ADD, |
2530 |
|
|
0, 0, -1, &pfm, sizeof(pfm)) == -1) |
2531 |
|
|
fatal("%s %d imsg_compose error", __func__, __LINE__); |
2532 |
|
|
} |
2533 |
|
|
|
2534 |
|
|
void |
2535 |
|
|
rde_send_pftable_commit(void) |
2536 |
|
|
{ |
2537 |
|
|
/* do not run while cleaning up */ |
2538 |
|
|
if (rde_quit) |
2539 |
|
|
return; |
2540 |
|
|
|
2541 |
|
|
if (imsg_compose(ibuf_main, IMSG_PFTABLE_COMMIT, 0, 0, -1, NULL, 0) == |
2542 |
|
|
-1) |
2543 |
|
|
fatal("%s %d imsg_compose error", __func__, __LINE__); |
2544 |
|
|
} |
2545 |
|
|
|
2546 |
|
|
/* |
2547 |
|
|
* nexthop specific functions |
2548 |
|
|
*/ |
2549 |
|
|
void |
2550 |
|
|
rde_send_nexthop(struct bgpd_addr *next, int valid) |
2551 |
|
|
{ |
2552 |
|
|
int type; |
2553 |
|
|
|
2554 |
|
|
if (valid) |
2555 |
|
|
type = IMSG_NEXTHOP_ADD; |
2556 |
|
|
else |
2557 |
|
|
type = IMSG_NEXTHOP_REMOVE; |
2558 |
|
|
|
2559 |
|
|
if (imsg_compose(ibuf_main, type, 0, 0, -1, next, |
2560 |
|
|
sizeof(struct bgpd_addr)) == -1) |
2561 |
|
|
fatal("%s %d imsg_compose error", __func__, __LINE__); |
2562 |
|
|
} |
2563 |
|
|
|
2564 |
|
|
/* |
2565 |
|
|
* soft reconfig specific functions |
2566 |
|
|
*/ |
2567 |
|
|
void |
2568 |
|
|
rde_reload_done(void) |
2569 |
|
|
{ |
2570 |
|
|
struct rdomain *rd; |
2571 |
|
|
struct rde_peer *peer; |
2572 |
|
|
struct filter_head *fh; |
2573 |
|
|
u_int16_t rid; |
2574 |
|
|
|
2575 |
|
|
/* first merge the main config */ |
2576 |
|
|
if ((nconf->flags & BGPD_FLAG_NO_EVALUATE) |
2577 |
|
|
!= (conf->flags & BGPD_FLAG_NO_EVALUATE)) { |
2578 |
|
|
log_warnx("change to/from route-collector " |
2579 |
|
|
"mode ignored"); |
2580 |
|
|
if (conf->flags & BGPD_FLAG_NO_EVALUATE) |
2581 |
|
|
nconf->flags |= BGPD_FLAG_NO_EVALUATE; |
2582 |
|
|
else |
2583 |
|
|
nconf->flags &= ~BGPD_FLAG_NO_EVALUATE; |
2584 |
|
|
} |
2585 |
|
|
memcpy(conf, nconf, sizeof(struct bgpd_config)); |
2586 |
|
|
conf->listen_addrs = NULL; |
2587 |
|
|
conf->csock = NULL; |
2588 |
|
|
conf->rcsock = NULL; |
2589 |
|
|
free(nconf); |
2590 |
|
|
nconf = NULL; |
2591 |
|
|
|
2592 |
|
|
/* sync peerself with conf */ |
2593 |
|
|
peerself->remote_bgpid = ntohl(conf->bgpid); |
2594 |
|
|
peerself->conf.local_as = conf->as; |
2595 |
|
|
peerself->conf.remote_as = conf->as; |
2596 |
|
|
peerself->short_as = conf->short_as; |
2597 |
|
|
|
2598 |
|
|
/* apply new set of rdomain, sync will be done later */ |
2599 |
|
|
while ((rd = SIMPLEQ_FIRST(rdomains_l)) != NULL) { |
2600 |
|
|
SIMPLEQ_REMOVE_HEAD(rdomains_l, entry); |
2601 |
|
|
filterset_free(&rd->import); |
2602 |
|
|
filterset_free(&rd->export); |
2603 |
|
|
free(rd); |
2604 |
|
|
} |
2605 |
|
|
free(rdomains_l); |
2606 |
|
|
rdomains_l = newdomains; |
2607 |
|
|
/* XXX WHERE IS THE SYNC ??? */ |
2608 |
|
|
|
2609 |
|
|
rde_filter_calc_skip_steps(out_rules_tmp); |
2610 |
|
|
|
2611 |
|
|
/* |
2612 |
|
|
* make the new filter rules the active one but keep the old for |
2613 |
|
|
* softrconfig. This is needed so that changes happening are using |
2614 |
|
|
* the right filters. |
2615 |
|
|
*/ |
2616 |
|
|
fh = out_rules; |
2617 |
|
|
out_rules = out_rules_tmp; |
2618 |
|
|
out_rules_tmp = fh; |
2619 |
|
|
|
2620 |
|
|
/* check if filter changed */ |
2621 |
|
|
LIST_FOREACH(peer, &peerlist, peer_l) { |
2622 |
|
|
if (peer->conf.id == 0) |
2623 |
|
|
continue; |
2624 |
|
|
peer->reconf_out = 0; |
2625 |
|
|
peer->reconf_rib = 0; |
2626 |
|
|
if (peer->ribid != rib_find(peer->conf.rib)) { |
2627 |
|
|
rib_dump(&ribs[peer->ribid], |
2628 |
|
|
rde_softreconfig_unload_peer, peer, AID_UNSPEC); |
2629 |
|
|
peer->ribid = rib_find(peer->conf.rib); |
2630 |
|
|
if (peer->ribid == RIB_FAILED) |
2631 |
|
|
fatalx("King Bula's peer met an unknown RIB"); |
2632 |
|
|
peer->reconf_rib = 1; |
2633 |
|
|
continue; |
2634 |
|
|
} |
2635 |
|
|
if (peer->conf.softreconfig_out && |
2636 |
|
|
!rde_filter_equal(out_rules, out_rules_tmp, peer)) { |
2637 |
|
|
peer->reconf_out = 1; |
2638 |
|
|
} |
2639 |
|
|
} |
2640 |
|
|
/* bring ribs in sync */ |
2641 |
|
|
for (rid = 0; rid < rib_size; rid++) { |
2642 |
|
|
if (*ribs[rid].name == '\0') |
2643 |
|
|
continue; |
2644 |
|
|
rde_filter_calc_skip_steps(ribs[rid].in_rules_tmp); |
2645 |
|
|
|
2646 |
|
|
/* flip rules, make new active */ |
2647 |
|
|
fh = ribs[rid].in_rules; |
2648 |
|
|
ribs[rid].in_rules = ribs[rid].in_rules_tmp; |
2649 |
|
|
ribs[rid].in_rules_tmp = fh; |
2650 |
|
|
|
2651 |
|
|
switch (ribs[rid].state) { |
2652 |
|
|
case RECONF_DELETE: |
2653 |
|
|
rib_free(&ribs[rid]); |
2654 |
|
|
break; |
2655 |
|
|
case RECONF_KEEP: |
2656 |
|
|
if (rde_filter_equal(ribs[rid].in_rules, |
2657 |
|
|
ribs[rid].in_rules_tmp, NULL)) |
2658 |
|
|
/* rib is in sync */ |
2659 |
|
|
break; |
2660 |
|
|
ribs[rid].state = RECONF_RELOAD; |
2661 |
|
|
/* FALLTHROUGH */ |
2662 |
|
|
case RECONF_REINIT: |
2663 |
|
|
rib_dump(&ribs[0], rde_softreconfig_in, &ribs[rid], |
2664 |
|
|
AID_UNSPEC); |
2665 |
|
|
break; |
2666 |
|
|
case RECONF_RELOAD: |
2667 |
|
|
log_warnx("Bad rib reload state"); |
2668 |
|
|
/* FALLTHROUGH */ |
2669 |
|
|
case RECONF_NONE: |
2670 |
|
|
break; |
2671 |
|
|
} |
2672 |
|
|
} |
2673 |
|
|
LIST_FOREACH(peer, &peerlist, peer_l) { |
2674 |
|
|
if (peer->reconf_out) |
2675 |
|
|
rib_dump(&ribs[peer->ribid], rde_softreconfig_out, |
2676 |
|
|
peer, AID_UNSPEC); |
2677 |
|
|
else if (peer->reconf_rib) |
2678 |
|
|
/* dump the full table to neighbors that changed rib */ |
2679 |
|
|
peer_dump(peer->conf.id, AID_UNSPEC); |
2680 |
|
|
} |
2681 |
|
|
filterlist_free(out_rules_tmp); |
2682 |
|
|
out_rules_tmp = NULL; |
2683 |
|
|
for (rid = 0; rid < rib_size; rid++) { |
2684 |
|
|
if (*ribs[rid].name == '\0') |
2685 |
|
|
continue; |
2686 |
|
|
filterlist_free(ribs[rid].in_rules_tmp); |
2687 |
|
|
ribs[rid].in_rules_tmp = NULL; |
2688 |
|
|
ribs[rid].state = RECONF_NONE; |
2689 |
|
|
} |
2690 |
|
|
|
2691 |
|
|
log_info("RDE reconfigured"); |
2692 |
|
|
imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0, |
2693 |
|
|
-1, NULL, 0); |
2694 |
|
|
} |
2695 |
|
|
|
2696 |
|
|
void |
2697 |
|
|
rde_softreconfig_in(struct rib_entry *re, void *ptr) |
2698 |
|
|
{ |
2699 |
|
|
struct rib *rib = ptr; |
2700 |
|
|
struct prefix *p, *np; |
2701 |
|
|
struct pt_entry *pt; |
2702 |
|
|
struct rde_peer *peer; |
2703 |
|
|
struct rde_aspath *asp, *oasp, *nasp; |
2704 |
|
|
enum filter_actions oa, na; |
2705 |
|
|
struct bgpd_addr addr; |
2706 |
|
|
|
2707 |
|
|
pt = re->prefix; |
2708 |
|
|
pt_getaddr(pt, &addr); |
2709 |
|
|
for (p = LIST_FIRST(&re->prefix_h); p != NULL; p = np) { |
2710 |
|
|
/* |
2711 |
|
|
* prefix_remove() and path_update() may change the object |
2712 |
|
|
* so cache the values. |
2713 |
|
|
*/ |
2714 |
|
|
np = LIST_NEXT(p, rib_l); |
2715 |
|
|
asp = p->aspath; |
2716 |
|
|
peer = asp->peer; |
2717 |
|
|
|
2718 |
|
|
/* check if prefix changed */ |
2719 |
|
|
if (rib->state == RECONF_RELOAD) { |
2720 |
|
|
oa = rde_filter(rib->in_rules_tmp, &oasp, peer, |
2721 |
|
|
asp, &addr, pt->prefixlen, peer); |
2722 |
|
|
oasp = oasp != NULL ? oasp : asp; |
2723 |
|
|
} else { |
2724 |
|
|
/* make sure we update everything for RECONF_REINIT */ |
2725 |
|
|
oa = ACTION_DENY; |
2726 |
|
|
oasp = asp; |
2727 |
|
|
} |
2728 |
|
|
na = rde_filter(rib->in_rules, &nasp, peer, asp, |
2729 |
|
|
&addr, pt->prefixlen, peer); |
2730 |
|
|
nasp = nasp != NULL ? nasp : asp; |
2731 |
|
|
|
2732 |
|
|
/* go through all 4 possible combinations */ |
2733 |
|
|
/* if (oa == ACTION_DENY && na == ACTION_DENY) */ |
2734 |
|
|
/* nothing todo */ |
2735 |
|
|
if (oa == ACTION_DENY && na == ACTION_ALLOW) { |
2736 |
|
|
/* update Local-RIB */ |
2737 |
|
|
path_update(rib, peer, nasp, &addr, pt->prefixlen); |
2738 |
|
|
} else if (oa == ACTION_ALLOW && na == ACTION_DENY) { |
2739 |
|
|
/* remove from Local-RIB */ |
2740 |
|
|
prefix_remove(rib, peer, &addr, pt->prefixlen, 0); |
2741 |
|
|
} else if (oa == ACTION_ALLOW && na == ACTION_ALLOW) { |
2742 |
|
|
if (path_compare(nasp, oasp) != 0) |
2743 |
|
|
/* send update */ |
2744 |
|
|
path_update(rib, peer, nasp, &addr, |
2745 |
|
|
pt->prefixlen); |
2746 |
|
|
} |
2747 |
|
|
|
2748 |
|
|
if (oasp != asp) |
2749 |
|
|
path_put(oasp); |
2750 |
|
|
if (nasp != asp) |
2751 |
|
|
path_put(nasp); |
2752 |
|
|
} |
2753 |
|
|
} |
2754 |
|
|
|
2755 |
|
|
void |
2756 |
|
|
rde_softreconfig_out(struct rib_entry *re, void *ptr) |
2757 |
|
|
{ |
2758 |
|
|
struct prefix *p = re->active; |
2759 |
|
|
struct pt_entry *pt; |
2760 |
|
|
struct rde_peer *peer = ptr; |
2761 |
|
|
struct rde_aspath *oasp, *nasp; |
2762 |
|
|
enum filter_actions oa, na; |
2763 |
|
|
struct bgpd_addr addr; |
2764 |
|
|
|
2765 |
|
|
if (peer->conf.id == 0) |
2766 |
|
|
fatalx("King Bula troubled by bad peer"); |
2767 |
|
|
|
2768 |
|
|
if (p == NULL) |
2769 |
|
|
return; |
2770 |
|
|
|
2771 |
|
|
pt = re->prefix; |
2772 |
|
|
pt_getaddr(pt, &addr); |
2773 |
|
|
|
2774 |
|
|
if (up_test_update(peer, p) != 1) |
2775 |
|
|
return; |
2776 |
|
|
|
2777 |
|
|
oa = rde_filter(out_rules_tmp, &oasp, peer, p->aspath, |
2778 |
|
|
&addr, pt->prefixlen, p->aspath->peer); |
2779 |
|
|
na = rde_filter(out_rules, &nasp, peer, p->aspath, |
2780 |
|
|
&addr, pt->prefixlen, p->aspath->peer); |
2781 |
|
|
oasp = oasp != NULL ? oasp : p->aspath; |
2782 |
|
|
nasp = nasp != NULL ? nasp : p->aspath; |
2783 |
|
|
|
2784 |
|
|
/* go through all 4 possible combinations */ |
2785 |
|
|
/* if (oa == ACTION_DENY && na == ACTION_DENY) */ |
2786 |
|
|
/* nothing todo */ |
2787 |
|
|
if (oa == ACTION_DENY && na == ACTION_ALLOW) { |
2788 |
|
|
/* send update */ |
2789 |
|
|
up_generate(peer, nasp, &addr, pt->prefixlen); |
2790 |
|
|
} else if (oa == ACTION_ALLOW && na == ACTION_DENY) { |
2791 |
|
|
/* send withdraw */ |
2792 |
|
|
up_generate(peer, NULL, &addr, pt->prefixlen); |
2793 |
|
|
} else if (oa == ACTION_ALLOW && na == ACTION_ALLOW) { |
2794 |
|
|
/* send update if path attributes changed */ |
2795 |
|
|
if (path_compare(nasp, oasp) != 0) |
2796 |
|
|
up_generate(peer, nasp, &addr, pt->prefixlen); |
2797 |
|
|
} |
2798 |
|
|
|
2799 |
|
|
if (oasp != p->aspath) |
2800 |
|
|
path_put(oasp); |
2801 |
|
|
if (nasp != p->aspath) |
2802 |
|
|
path_put(nasp); |
2803 |
|
|
} |
2804 |
|
|
|
2805 |
|
|
void |
2806 |
|
|
rde_softreconfig_unload_peer(struct rib_entry *re, void *ptr) |
2807 |
|
|
{ |
2808 |
|
|
struct rde_peer *peer = ptr; |
2809 |
|
|
struct prefix *p = re->active; |
2810 |
|
|
struct pt_entry *pt; |
2811 |
|
|
struct rde_aspath *oasp; |
2812 |
|
|
enum filter_actions oa; |
2813 |
|
|
struct bgpd_addr addr; |
2814 |
|
|
|
2815 |
|
|
pt = re->prefix; |
2816 |
|
|
pt_getaddr(pt, &addr); |
2817 |
|
|
|
2818 |
|
|
/* check if prefix was announced */ |
2819 |
|
|
if (up_test_update(peer, p) != 1) |
2820 |
|
|
return; |
2821 |
|
|
|
2822 |
|
|
oa = rde_filter(out_rules_tmp, &oasp, peer, p->aspath, |
2823 |
|
|
&addr, pt->prefixlen, p->aspath->peer); |
2824 |
|
|
oasp = oasp != NULL ? oasp : p->aspath; |
2825 |
|
|
|
2826 |
|
|
if (oa == ACTION_DENY) |
2827 |
|
|
/* nothing todo */ |
2828 |
|
|
goto done; |
2829 |
|
|
|
2830 |
|
|
/* send withdraw */ |
2831 |
|
|
up_generate(peer, NULL, &addr, pt->prefixlen); |
2832 |
|
|
done: |
2833 |
|
|
if (oasp != p->aspath) |
2834 |
|
|
path_put(oasp); |
2835 |
|
|
} |
2836 |
|
|
|
2837 |
|
|
/* |
2838 |
|
|
* update specific functions |
2839 |
|
|
*/ |
2840 |
|
|
u_char queue_buf[4096]; |
2841 |
|
|
|
2842 |
|
|
void |
2843 |
|
|
rde_up_dump_upcall(struct rib_entry *re, void *ptr) |
2844 |
|
|
{ |
2845 |
|
|
struct rde_peer *peer = ptr; |
2846 |
|
|
|
2847 |
|
|
if (re->ribid != peer->ribid) |
2848 |
|
|
fatalx("King Bula: monstrous evil horror."); |
2849 |
|
|
if (re->active == NULL) |
2850 |
|
|
return; |
2851 |
|
|
up_generate_updates(out_rules, peer, re->active, NULL); |
2852 |
|
|
} |
2853 |
|
|
|
2854 |
|
|
void |
2855 |
|
|
rde_generate_updates(u_int16_t ribid, struct prefix *new, struct prefix *old) |
2856 |
|
|
{ |
2857 |
|
|
struct rde_peer *peer; |
2858 |
|
|
|
2859 |
|
|
/* |
2860 |
|
|
* If old is != NULL we know it was active and should be removed. |
2861 |
|
|
* If new is != NULL we know it is reachable and then we should |
2862 |
|
|
* generate an update. |
2863 |
|
|
*/ |
2864 |
|
|
if (old == NULL && new == NULL) |
2865 |
|
|
return; |
2866 |
|
|
|
2867 |
|
|
LIST_FOREACH(peer, &peerlist, peer_l) { |
2868 |
|
|
if (peer->conf.id == 0) |
2869 |
|
|
continue; |
2870 |
|
|
if (peer->ribid != ribid) |
2871 |
|
|
continue; |
2872 |
|
|
if (peer->state != PEER_UP) |
2873 |
|
|
continue; |
2874 |
|
|
up_generate_updates(out_rules, peer, new, old); |
2875 |
|
|
} |
2876 |
|
|
} |
2877 |
|
|
|
2878 |
|
|
void |
2879 |
|
|
rde_update_queue_runner(void) |
2880 |
|
|
{ |
2881 |
|
|
struct rde_peer *peer; |
2882 |
|
|
int r, sent, max = RDE_RUNNER_ROUNDS, eor = 0; |
2883 |
|
|
u_int16_t len, wd_len, wpos; |
2884 |
|
|
|
2885 |
|
|
len = sizeof(queue_buf) - MSGSIZE_HEADER; |
2886 |
|
|
do { |
2887 |
|
|
sent = 0; |
2888 |
|
|
LIST_FOREACH(peer, &peerlist, peer_l) { |
2889 |
|
|
if (peer->conf.id == 0) |
2890 |
|
|
continue; |
2891 |
|
|
if (peer->state != PEER_UP) |
2892 |
|
|
continue; |
2893 |
|
|
/* first withdraws */ |
2894 |
|
|
wpos = 2; /* reserve space for the length field */ |
2895 |
|
|
r = up_dump_prefix(queue_buf + wpos, len - wpos - 2, |
2896 |
|
|
&peer->withdraws[AID_INET], peer); |
2897 |
|
|
wd_len = r; |
2898 |
|
|
/* write withdraws length filed */ |
2899 |
|
|
wd_len = htons(wd_len); |
2900 |
|
|
memcpy(queue_buf, &wd_len, 2); |
2901 |
|
|
wpos += r; |
2902 |
|
|
|
2903 |
|
|
/* now bgp path attributes */ |
2904 |
|
|
r = up_dump_attrnlri(queue_buf + wpos, len - wpos, |
2905 |
|
|
peer); |
2906 |
|
|
switch (r) { |
2907 |
|
|
case -1: |
2908 |
|
|
eor = 1; |
2909 |
|
|
if (wd_len == 0) { |
2910 |
|
|
/* no withdraws queued just send EoR */ |
2911 |
|
|
peer_send_eor(peer, AID_INET); |
2912 |
|
|
continue; |
2913 |
|
|
} |
2914 |
|
|
break; |
2915 |
|
|
case 2: |
2916 |
|
|
if (wd_len == 0) { |
2917 |
|
|
/* |
2918 |
|
|
* No packet to send. No withdraws and |
2919 |
|
|
* no path attributes. Skip. |
2920 |
|
|
*/ |
2921 |
|
|
continue; |
2922 |
|
|
} |
2923 |
|
|
/* FALLTHROUGH */ |
2924 |
|
|
default: |
2925 |
|
|
wpos += r; |
2926 |
|
|
break; |
2927 |
|
|
} |
2928 |
|
|
|
2929 |
|
|
/* finally send message to SE */ |
2930 |
|
|
if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, |
2931 |
|
|
0, -1, queue_buf, wpos) == -1) |
2932 |
|
|
fatal("%s %d imsg_compose error", __func__, |
2933 |
|
|
__LINE__); |
2934 |
|
|
sent++; |
2935 |
|
|
if (eor) { |
2936 |
|
|
eor = 0; |
2937 |
|
|
peer_send_eor(peer, AID_INET); |
2938 |
|
|
} |
2939 |
|
|
} |
2940 |
|
|
max -= sent; |
2941 |
|
|
} while (sent != 0 && max > 0); |
2942 |
|
|
} |
2943 |
|
|
|
2944 |
|
|
void |
2945 |
|
|
rde_update6_queue_runner(u_int8_t aid) |
2946 |
|
|
{ |
2947 |
|
|
struct rde_peer *peer; |
2948 |
|
|
u_char *b; |
2949 |
|
|
int r, sent, max = RDE_RUNNER_ROUNDS / 2; |
2950 |
|
|
u_int16_t len; |
2951 |
|
|
|
2952 |
|
|
/* first withdraws ... */ |
2953 |
|
|
do { |
2954 |
|
|
sent = 0; |
2955 |
|
|
LIST_FOREACH(peer, &peerlist, peer_l) { |
2956 |
|
|
if (peer->conf.id == 0) |
2957 |
|
|
continue; |
2958 |
|
|
if (peer->state != PEER_UP) |
2959 |
|
|
continue; |
2960 |
|
|
len = sizeof(queue_buf) - MSGSIZE_HEADER; |
2961 |
|
|
b = up_dump_mp_unreach(queue_buf, &len, peer, aid); |
2962 |
|
|
|
2963 |
|
|
if (b == NULL) |
2964 |
|
|
continue; |
2965 |
|
|
/* finally send message to SE */ |
2966 |
|
|
if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, |
2967 |
|
|
0, -1, b, len) == -1) |
2968 |
|
|
fatal("%s %d imsg_compose error", __func__, |
2969 |
|
|
__LINE__); |
2970 |
|
|
sent++; |
2971 |
|
|
} |
2972 |
|
|
max -= sent; |
2973 |
|
|
} while (sent != 0 && max > 0); |
2974 |
|
|
|
2975 |
|
|
/* ... then updates */ |
2976 |
|
|
max = RDE_RUNNER_ROUNDS / 2; |
2977 |
|
|
do { |
2978 |
|
|
sent = 0; |
2979 |
|
|
LIST_FOREACH(peer, &peerlist, peer_l) { |
2980 |
|
|
if (peer->conf.id == 0) |
2981 |
|
|
continue; |
2982 |
|
|
if (peer->state != PEER_UP) |
2983 |
|
|
continue; |
2984 |
|
|
len = sizeof(queue_buf) - MSGSIZE_HEADER; |
2985 |
|
|
r = up_dump_mp_reach(queue_buf, &len, peer, aid); |
2986 |
|
|
switch (r) { |
2987 |
|
|
case -2: |
2988 |
|
|
continue; |
2989 |
|
|
case -1: |
2990 |
|
|
peer_send_eor(peer, aid); |
2991 |
|
|
continue; |
2992 |
|
|
default: |
2993 |
|
|
b = queue_buf + r; |
2994 |
|
|
break; |
2995 |
|
|
} |
2996 |
|
|
|
2997 |
|
|
/* finally send message to SE */ |
2998 |
|
|
if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, |
2999 |
|
|
0, -1, b, len) == -1) |
3000 |
|
|
fatal("%s %d imsg_compose error", __func__, |
3001 |
|
|
__LINE__); |
3002 |
|
|
sent++; |
3003 |
|
|
} |
3004 |
|
|
max -= sent; |
3005 |
|
|
} while (sent != 0 && max > 0); |
3006 |
|
|
} |
3007 |
|
|
|
3008 |
|
|
/* |
3009 |
|
|
* generic helper function |
3010 |
|
|
*/ |
3011 |
|
|
u_int32_t |
3012 |
|
|
rde_local_as(void) |
3013 |
|
|
{ |
3014 |
|
|
return (conf->as); |
3015 |
|
|
} |
3016 |
|
|
|
3017 |
|
|
int |
3018 |
|
|
rde_noevaluate(void) |
3019 |
|
|
{ |
3020 |
|
|
/* do not run while cleaning up */ |
3021 |
|
|
if (rde_quit) |
3022 |
|
|
return (1); |
3023 |
|
|
|
3024 |
|
|
return (conf->flags & BGPD_FLAG_NO_EVALUATE); |
3025 |
|
|
} |
3026 |
|
|
|
3027 |
|
|
int |
3028 |
|
|
rde_decisionflags(void) |
3029 |
|
|
{ |
3030 |
|
|
return (conf->flags & BGPD_FLAG_DECISION_MASK); |
3031 |
|
|
} |
3032 |
|
|
|
3033 |
|
|
int |
3034 |
|
|
rde_as4byte(struct rde_peer *peer) |
3035 |
|
|
{ |
3036 |
|
|
return (peer->capa.as4byte); |
3037 |
|
|
} |
3038 |
|
|
|
3039 |
|
|
/* |
3040 |
|
|
* peer functions |
3041 |
|
|
*/ |
3042 |
|
|
struct peer_table { |
3043 |
|
|
struct rde_peer_head *peer_hashtbl; |
3044 |
|
|
u_int32_t peer_hashmask; |
3045 |
|
|
} peertable; |
3046 |
|
|
|
3047 |
|
|
#define PEER_HASH(x) \ |
3048 |
|
|
&peertable.peer_hashtbl[(x) & peertable.peer_hashmask] |
3049 |
|
|
|
3050 |
|
|
void |
3051 |
|
|
peer_init(u_int32_t hashsize) |
3052 |
|
|
{ |
3053 |
|
|
struct peer_config pc; |
3054 |
|
|
u_int32_t hs, i; |
3055 |
|
|
|
3056 |
|
|
for (hs = 1; hs < hashsize; hs <<= 1) |
3057 |
|
|
; |
3058 |
|
|
peertable.peer_hashtbl = calloc(hs, sizeof(struct rde_peer_head)); |
3059 |
|
|
if (peertable.peer_hashtbl == NULL) |
3060 |
|
|
fatal("peer_init"); |
3061 |
|
|
|
3062 |
|
|
for (i = 0; i < hs; i++) |
3063 |
|
|
LIST_INIT(&peertable.peer_hashtbl[i]); |
3064 |
|
|
LIST_INIT(&peerlist); |
3065 |
|
|
|
3066 |
|
|
peertable.peer_hashmask = hs - 1; |
3067 |
|
|
|
3068 |
|
|
bzero(&pc, sizeof(pc)); |
3069 |
|
|
snprintf(pc.descr, sizeof(pc.descr), "LOCAL"); |
3070 |
|
|
|
3071 |
|
|
peerself = peer_add(0, &pc); |
3072 |
|
|
if (peerself == NULL) |
3073 |
|
|
fatalx("peer_init add self"); |
3074 |
|
|
|
3075 |
|
|
peerself->state = PEER_UP; |
3076 |
|
|
} |
3077 |
|
|
|
3078 |
|
|
void |
3079 |
|
|
peer_shutdown(void) |
3080 |
|
|
{ |
3081 |
|
|
u_int32_t i; |
3082 |
|
|
|
3083 |
|
|
for (i = 0; i <= peertable.peer_hashmask; i++) |
3084 |
|
|
if (!LIST_EMPTY(&peertable.peer_hashtbl[i])) |
3085 |
|
|
log_warnx("peer_free: free non-free table"); |
3086 |
|
|
|
3087 |
|
|
free(peertable.peer_hashtbl); |
3088 |
|
|
} |
3089 |
|
|
|
3090 |
|
|
struct rde_peer * |
3091 |
|
|
peer_get(u_int32_t id) |
3092 |
|
|
{ |
3093 |
|
|
struct rde_peer_head *head; |
3094 |
|
|
struct rde_peer *peer; |
3095 |
|
|
|
3096 |
|
|
head = PEER_HASH(id); |
3097 |
|
|
|
3098 |
|
|
LIST_FOREACH(peer, head, hash_l) { |
3099 |
|
|
if (peer->conf.id == id) |
3100 |
|
|
return (peer); |
3101 |
|
|
} |
3102 |
|
|
return (NULL); |
3103 |
|
|
} |
3104 |
|
|
|
3105 |
|
|
struct rde_peer * |
3106 |
|
|
peer_add(u_int32_t id, struct peer_config *p_conf) |
3107 |
|
|
{ |
3108 |
|
|
struct rde_peer_head *head; |
3109 |
|
|
struct rde_peer *peer; |
3110 |
|
|
|
3111 |
|
|
if ((peer = peer_get(id))) { |
3112 |
|
|
memcpy(&peer->conf, p_conf, sizeof(struct peer_config)); |
3113 |
|
|
return (NULL); |
3114 |
|
|
} |
3115 |
|
|
|
3116 |
|
|
peer = calloc(1, sizeof(struct rde_peer)); |
3117 |
|
|
if (peer == NULL) |
3118 |
|
|
fatal("peer_add"); |
3119 |
|
|
|
3120 |
|
|
LIST_INIT(&peer->path_h); |
3121 |
|
|
memcpy(&peer->conf, p_conf, sizeof(struct peer_config)); |
3122 |
|
|
peer->remote_bgpid = 0; |
3123 |
|
|
peer->ribid = rib_find(peer->conf.rib); |
3124 |
|
|
if (peer->ribid == RIB_FAILED) |
3125 |
|
|
fatalx("King Bula's new peer met an unknown RIB"); |
3126 |
|
|
peer->state = PEER_NONE; |
3127 |
|
|
up_init(peer); |
3128 |
|
|
|
3129 |
|
|
head = PEER_HASH(id); |
3130 |
|
|
|
3131 |
|
|
LIST_INSERT_HEAD(head, peer, hash_l); |
3132 |
|
|
LIST_INSERT_HEAD(&peerlist, peer, peer_l); |
3133 |
|
|
|
3134 |
|
|
return (peer); |
3135 |
|
|
} |
3136 |
|
|
|
3137 |
|
|
int |
3138 |
|
|
peer_localaddrs(struct rde_peer *peer, struct bgpd_addr *laddr) |
3139 |
|
|
{ |
3140 |
|
|
struct ifaddrs *ifap, *ifa, *match; |
3141 |
|
|
|
3142 |
|
|
if (getifaddrs(&ifap) == -1) |
3143 |
|
|
fatal("getifaddrs"); |
3144 |
|
|
|
3145 |
|
|
for (match = ifap; match != NULL; match = match->ifa_next) |
3146 |
|
|
if (sa_cmp(laddr, match->ifa_addr) == 0) |
3147 |
|
|
break; |
3148 |
|
|
|
3149 |
|
|
if (match == NULL) { |
3150 |
|
|
log_warnx("peer_localaddrs: local address not found"); |
3151 |
|
|
return (-1); |
3152 |
|
|
} |
3153 |
|
|
|
3154 |
|
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { |
3155 |
|
|
if (ifa->ifa_addr->sa_family == AF_INET && |
3156 |
|
|
strcmp(ifa->ifa_name, match->ifa_name) == 0) { |
3157 |
|
|
if (ifa->ifa_addr->sa_family == |
3158 |
|
|
match->ifa_addr->sa_family) |
3159 |
|
|
ifa = match; |
3160 |
|
|
sa2addr(ifa->ifa_addr, &peer->local_v4_addr); |
3161 |
|
|
break; |
3162 |
|
|
} |
3163 |
|
|
} |
3164 |
|
|
for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { |
3165 |
|
|
if (ifa->ifa_addr->sa_family == AF_INET6 && |
3166 |
|
|
strcmp(ifa->ifa_name, match->ifa_name) == 0) { |
3167 |
|
|
/* |
3168 |
|
|
* only accept global scope addresses except explicitly |
3169 |
|
|
* specified. |
3170 |
|
|
*/ |
3171 |
|
|
if (ifa->ifa_addr->sa_family == |
3172 |
|
|
match->ifa_addr->sa_family) |
3173 |
|
|
ifa = match; |
3174 |
|
|
else if (IN6_IS_ADDR_LINKLOCAL( |
3175 |
|
|
&((struct sockaddr_in6 *)ifa-> |
3176 |
|
|
ifa_addr)->sin6_addr) || |
3177 |
|
|
IN6_IS_ADDR_SITELOCAL( |
3178 |
|
|
&((struct sockaddr_in6 *)ifa-> |
3179 |
|
|
ifa_addr)->sin6_addr)) |
3180 |
|
|
continue; |
3181 |
|
|
sa2addr(ifa->ifa_addr, &peer->local_v6_addr); |
3182 |
|
|
break; |
3183 |
|
|
} |
3184 |
|
|
} |
3185 |
|
|
|
3186 |
|
|
freeifaddrs(ifap); |
3187 |
|
|
return (0); |
3188 |
|
|
} |
3189 |
|
|
|
3190 |
|
|
void |
3191 |
|
|
peer_up(u_int32_t id, struct session_up *sup) |
3192 |
|
|
{ |
3193 |
|
|
struct rde_peer *peer; |
3194 |
|
|
u_int8_t i; |
3195 |
|
|
|
3196 |
|
|
peer = peer_get(id); |
3197 |
|
|
if (peer == NULL) { |
3198 |
|
|
log_warnx("peer_up: unknown peer id %d", id); |
3199 |
|
|
return; |
3200 |
|
|
} |
3201 |
|
|
|
3202 |
|
|
if (peer->state != PEER_DOWN && peer->state != PEER_NONE && |
3203 |
|
|
peer->state != PEER_UP) { |
3204 |
|
|
/* |
3205 |
|
|
* There is a race condition when doing PEER_ERR -> PEER_DOWN. |
3206 |
|
|
* So just do a full reset of the peer here. |
3207 |
|
|
*/ |
3208 |
|
|
for (i = 0; i < AID_MAX; i++) { |
3209 |
|
|
peer->staletime[i] = 0; |
3210 |
|
|
peer_flush(peer, i); |
3211 |
|
|
} |
3212 |
|
|
up_down(peer); |
3213 |
|
|
peer->prefix_cnt = 0; |
3214 |
|
|
peer->state = PEER_DOWN; |
3215 |
|
|
} |
3216 |
|
|
peer->remote_bgpid = ntohl(sup->remote_bgpid); |
3217 |
|
|
peer->short_as = sup->short_as; |
3218 |
|
|
memcpy(&peer->remote_addr, &sup->remote_addr, |
3219 |
|
|
sizeof(peer->remote_addr)); |
3220 |
|
|
memcpy(&peer->capa, &sup->capa, sizeof(peer->capa)); |
3221 |
|
|
|
3222 |
|
|
if (peer_localaddrs(peer, &sup->local_addr)) { |
3223 |
|
|
peer->state = PEER_DOWN; |
3224 |
|
|
imsg_compose(ibuf_se, IMSG_SESSION_DOWN, id, 0, -1, NULL, 0); |
3225 |
|
|
return; |
3226 |
|
|
} |
3227 |
|
|
|
3228 |
|
|
peer->state = PEER_UP; |
3229 |
|
|
up_init(peer); |
3230 |
|
|
|
3231 |
|
|
if (rde_noevaluate()) |
3232 |
|
|
/* |
3233 |
|
|
* no need to dump the table to the peer, there are no active |
3234 |
|
|
* prefixes anyway. This is a speed up hack. |
3235 |
|
|
*/ |
3236 |
|
|
return; |
3237 |
|
|
|
3238 |
|
|
for (i = 0; i < AID_MAX; i++) { |
3239 |
|
|
if (peer->capa.mp[i]) |
3240 |
|
|
peer_dump(id, i); |
3241 |
|
|
} |
3242 |
|
|
} |
3243 |
|
|
|
3244 |
|
|
void |
3245 |
|
|
peer_down(u_int32_t id) |
3246 |
|
|
{ |
3247 |
|
|
struct rde_peer *peer; |
3248 |
|
|
struct rde_aspath *asp, *nasp; |
3249 |
|
|
|
3250 |
|
|
peer = peer_get(id); |
3251 |
|
|
if (peer == NULL) { |
3252 |
|
|
log_warnx("peer_down: unknown peer id %d", id); |
3253 |
|
|
return; |
3254 |
|
|
} |
3255 |
|
|
peer->remote_bgpid = 0; |
3256 |
|
|
peer->state = PEER_DOWN; |
3257 |
|
|
up_down(peer); |
3258 |
|
|
|
3259 |
|
|
/* walk through per peer RIB list and remove all prefixes. */ |
3260 |
|
|
for (asp = LIST_FIRST(&peer->path_h); asp != NULL; asp = nasp) { |
3261 |
|
|
nasp = LIST_NEXT(asp, peer_l); |
3262 |
|
|
path_remove(asp); |
3263 |
|
|
} |
3264 |
|
|
LIST_INIT(&peer->path_h); |
3265 |
|
|
peer->prefix_cnt = 0; |
3266 |
|
|
|
3267 |
|
|
/* Deletions are performed in path_remove() */ |
3268 |
|
|
rde_send_pftable_commit(); |
3269 |
|
|
|
3270 |
|
|
LIST_REMOVE(peer, hash_l); |
3271 |
|
|
LIST_REMOVE(peer, peer_l); |
3272 |
|
|
free(peer); |
3273 |
|
|
} |
3274 |
|
|
|
3275 |
|
|
/* |
3276 |
|
|
* Flush all routes older then staletime. If staletime is 0 all routes will |
3277 |
|
|
* be flushed. |
3278 |
|
|
*/ |
3279 |
|
|
void |
3280 |
|
|
peer_flush(struct rde_peer *peer, u_int8_t aid) |
3281 |
|
|
{ |
3282 |
|
|
struct rde_aspath *asp, *nasp; |
3283 |
|
|
u_int32_t rprefixes; |
3284 |
|
|
|
3285 |
|
|
rprefixes = 0; |
3286 |
|
|
/* walk through per peer RIB list and remove all stale prefixes. */ |
3287 |
|
|
for (asp = LIST_FIRST(&peer->path_h); asp != NULL; asp = nasp) { |
3288 |
|
|
nasp = LIST_NEXT(asp, peer_l); |
3289 |
|
|
rprefixes += path_remove_stale(asp, aid); |
3290 |
|
|
} |
3291 |
|
|
|
3292 |
|
|
/* Deletions are performed in path_remove() */ |
3293 |
|
|
rde_send_pftable_commit(); |
3294 |
|
|
|
3295 |
|
|
/* flushed no need to keep staletime */ |
3296 |
|
|
peer->staletime[aid] = 0; |
3297 |
|
|
|
3298 |
|
|
if (peer->prefix_cnt > rprefixes) |
3299 |
|
|
peer->prefix_cnt -= rprefixes; |
3300 |
|
|
else |
3301 |
|
|
peer->prefix_cnt = 0; |
3302 |
|
|
} |
3303 |
|
|
|
3304 |
|
|
void |
3305 |
|
|
peer_stale(u_int32_t id, u_int8_t aid) |
3306 |
|
|
{ |
3307 |
|
|
struct rde_peer *peer; |
3308 |
|
|
time_t now; |
3309 |
|
|
|
3310 |
|
|
peer = peer_get(id); |
3311 |
|
|
if (peer == NULL) { |
3312 |
|
|
log_warnx("peer_stale: unknown peer id %d", id); |
3313 |
|
|
return; |
3314 |
|
|
} |
3315 |
|
|
|
3316 |
|
|
/* flush the now even staler routes out */ |
3317 |
|
|
if (peer->staletime[aid]) |
3318 |
|
|
peer_flush(peer, aid); |
3319 |
|
|
peer->staletime[aid] = now = time(NULL); |
3320 |
|
|
|
3321 |
|
|
/* make sure new prefixes start on a higher timestamp */ |
3322 |
|
|
do { |
3323 |
|
|
sleep(1); |
3324 |
|
|
} while (now >= time(NULL)); |
3325 |
|
|
} |
3326 |
|
|
|
3327 |
|
|
void |
3328 |
|
|
peer_dump(u_int32_t id, u_int8_t aid) |
3329 |
|
|
{ |
3330 |
|
|
struct rde_peer *peer; |
3331 |
|
|
|
3332 |
|
|
peer = peer_get(id); |
3333 |
|
|
if (peer == NULL) { |
3334 |
|
|
log_warnx("peer_dump: unknown peer id %d", id); |
3335 |
|
|
return; |
3336 |
|
|
} |
3337 |
|
|
|
3338 |
|
|
if (peer->conf.announce_type == ANNOUNCE_DEFAULT_ROUTE) |
3339 |
|
|
up_generate_default(out_rules, peer, aid); |
3340 |
|
|
else |
3341 |
|
|
rib_dump(&ribs[peer->ribid], rde_up_dump_upcall, peer, aid); |
3342 |
|
|
if (peer->capa.grestart.restart) |
3343 |
|
|
up_generate_marker(peer, aid); |
3344 |
|
|
} |
3345 |
|
|
|
3346 |
|
|
/* End-of-RIB marker, RFC 4724 */ |
3347 |
|
|
void |
3348 |
|
|
peer_recv_eor(struct rde_peer *peer, u_int8_t aid) |
3349 |
|
|
{ |
3350 |
|
|
peer->prefix_rcvd_eor++; |
3351 |
|
|
|
3352 |
|
|
/* |
3353 |
|
|
* First notify SE to avert a possible race with the restart timeout. |
3354 |
|
|
* If the timeout fires before this imsg is processed by the SE it will |
3355 |
|
|
* result in the same operation since the timeout issues a FLUSH which |
3356 |
|
|
* does the same as the RESTARTED action (flushing stale routes). |
3357 |
|
|
* The logic in the SE is so that only one of FLUSH or RESTARTED will |
3358 |
|
|
* be sent back to the RDE and so peer_flush is only called once. |
3359 |
|
|
*/ |
3360 |
|
|
if (imsg_compose(ibuf_se, IMSG_SESSION_RESTARTED, peer->conf.id, |
3361 |
|
|
0, -1, &aid, sizeof(aid)) == -1) |
3362 |
|
|
fatal("%s %d imsg_compose error", __func__, __LINE__); |
3363 |
|
|
} |
3364 |
|
|
|
3365 |
|
|
void |
3366 |
|
|
peer_send_eor(struct rde_peer *peer, u_int8_t aid) |
3367 |
|
|
{ |
3368 |
|
|
u_int16_t afi; |
3369 |
|
|
u_int8_t safi; |
3370 |
|
|
|
3371 |
|
|
peer->prefix_sent_eor++; |
3372 |
|
|
|
3373 |
|
|
if (aid == AID_INET) { |
3374 |
|
|
u_char null[4]; |
3375 |
|
|
|
3376 |
|
|
bzero(&null, 4); |
3377 |
|
|
if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, |
3378 |
|
|
0, -1, &null, 4) == -1) |
3379 |
|
|
fatal("%s %d imsg_compose error in peer_send_eor", |
3380 |
|
|
__func__, __LINE__); |
3381 |
|
|
} else { |
3382 |
|
|
u_int16_t i; |
3383 |
|
|
u_char buf[10]; |
3384 |
|
|
|
3385 |
|
|
if (aid2afi(aid, &afi, &safi) == -1) |
3386 |
|
|
fatalx("peer_send_eor: bad AID"); |
3387 |
|
|
|
3388 |
|
|
i = 0; /* v4 withdrawn len */ |
3389 |
|
|
bcopy(&i, &buf[0], sizeof(i)); |
3390 |
|
|
i = htons(6); /* path attr len */ |
3391 |
|
|
bcopy(&i, &buf[2], sizeof(i)); |
3392 |
|
|
buf[4] = ATTR_OPTIONAL; |
3393 |
|
|
buf[5] = ATTR_MP_UNREACH_NLRI; |
3394 |
|
|
buf[6] = 3; /* withdrawn len */ |
3395 |
|
|
i = htons(afi); |
3396 |
|
|
bcopy(&i, &buf[7], sizeof(i)); |
3397 |
|
|
buf[9] = safi; |
3398 |
|
|
|
3399 |
|
|
if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id, |
3400 |
|
|
0, -1, &buf, 10) == -1) |
3401 |
|
|
fatal("%s %d imsg_compose error in peer_send_eor", |
3402 |
|
|
__func__, __LINE__); |
3403 |
|
|
} |
3404 |
|
|
} |
3405 |
|
|
|
3406 |
|
|
/* |
3407 |
|
|
* network announcement stuff |
3408 |
|
|
*/ |
3409 |
|
|
void |
3410 |
|
|
network_add(struct network_config *nc, int flagstatic) |
3411 |
|
|
{ |
3412 |
|
|
struct rdomain *rd; |
3413 |
|
|
struct rde_aspath *asp; |
3414 |
|
|
struct filter_set_head *vpnset = NULL; |
3415 |
|
|
in_addr_t prefix4; |
3416 |
|
|
u_int16_t i; |
3417 |
|
|
|
3418 |
|
|
if (nc->rtableid) { |
3419 |
|
|
SIMPLEQ_FOREACH(rd, rdomains_l, entry) { |
3420 |
|
|
if (rd->rtableid != nc->rtableid) |
3421 |
|
|
continue; |
3422 |
|
|
switch (nc->prefix.aid) { |
3423 |
|
|
case AID_INET: |
3424 |
|
|
prefix4 = nc->prefix.v4.s_addr; |
3425 |
|
|
bzero(&nc->prefix, sizeof(nc->prefix)); |
3426 |
|
|
nc->prefix.aid = AID_VPN_IPv4; |
3427 |
|
|
nc->prefix.vpn4.rd = rd->rd; |
3428 |
|
|
nc->prefix.vpn4.addr.s_addr = prefix4; |
3429 |
|
|
nc->prefix.vpn4.labellen = 3; |
3430 |
|
|
nc->prefix.vpn4.labelstack[0] = |
3431 |
|
|
(rd->label >> 12) & 0xff; |
3432 |
|
|
nc->prefix.vpn4.labelstack[1] = |
3433 |
|
|
(rd->label >> 4) & 0xff; |
3434 |
|
|
nc->prefix.vpn4.labelstack[2] = |
3435 |
|
|
(rd->label << 4) & 0xf0; |
3436 |
|
|
nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS; |
3437 |
|
|
vpnset = &rd->export; |
3438 |
|
|
break; |
3439 |
|
|
default: |
3440 |
|
|
log_warnx("unable to VPNize prefix"); |
3441 |
|
|
filterset_free(&nc->attrset); |
3442 |
|
|
return; |
3443 |
|
|
} |
3444 |
|
|
break; |
3445 |
|
|
} |
3446 |
|
|
if (rd == NULL) { |
3447 |
|
|
log_warnx("network_add: " |
3448 |
|
|
"prefix %s/%u in non-existing rdomain %u", |
3449 |
|
|
log_addr(&nc->prefix), nc->prefixlen, nc->rtableid); |
3450 |
|
|
return; |
3451 |
|
|
} |
3452 |
|
|
} |
3453 |
|
|
|
3454 |
|
|
if (nc->type == NETWORK_MRTCLONE) { |
3455 |
|
|
asp = nc->asp; |
3456 |
|
|
} else { |
3457 |
|
|
asp = path_get(); |
3458 |
|
|
asp->aspath = aspath_get(NULL, 0); |
3459 |
|
|
asp->origin = ORIGIN_IGP; |
3460 |
|
|
asp->flags = F_ATTR_ORIGIN | F_ATTR_ASPATH | |
3461 |
|
|
F_ATTR_LOCALPREF | F_PREFIX_ANNOUNCED; |
3462 |
|
|
/* the nexthop is unset unless a default set overrides it */ |
3463 |
|
|
} |
3464 |
|
|
if (!flagstatic) |
3465 |
|
|
asp->flags |= F_ANN_DYNAMIC; |
3466 |
|
|
rde_apply_set(asp, &nc->attrset, nc->prefix.aid, peerself, peerself); |
3467 |
|
|
if (vpnset) |
3468 |
|
|
rde_apply_set(asp, vpnset, nc->prefix.aid, peerself, peerself); |
3469 |
|
|
for (i = 1; i < rib_size; i++) { |
3470 |
|
|
if (*ribs[i].name == '\0') |
3471 |
|
|
break; |
3472 |
|
|
path_update(&ribs[i], peerself, asp, &nc->prefix, |
3473 |
|
|
nc->prefixlen); |
3474 |
|
|
} |
3475 |
|
|
path_put(asp); |
3476 |
|
|
filterset_free(&nc->attrset); |
3477 |
|
|
} |
3478 |
|
|
|
3479 |
|
|
void |
3480 |
|
|
network_delete(struct network_config *nc, int flagstatic) |
3481 |
|
|
{ |
3482 |
|
|
struct rdomain *rd; |
3483 |
|
|
in_addr_t prefix4; |
3484 |
|
|
u_int32_t flags = F_PREFIX_ANNOUNCED; |
3485 |
|
|
u_int32_t i; |
3486 |
|
|
|
3487 |
|
|
if (!flagstatic) |
3488 |
|
|
flags |= F_ANN_DYNAMIC; |
3489 |
|
|
|
3490 |
|
|
if (nc->rtableid) { |
3491 |
|
|
SIMPLEQ_FOREACH(rd, rdomains_l, entry) { |
3492 |
|
|
if (rd->rtableid != nc->rtableid) |
3493 |
|
|
continue; |
3494 |
|
|
switch (nc->prefix.aid) { |
3495 |
|
|
case AID_INET: |
3496 |
|
|
prefix4 = nc->prefix.v4.s_addr; |
3497 |
|
|
bzero(&nc->prefix, sizeof(nc->prefix)); |
3498 |
|
|
nc->prefix.aid = AID_VPN_IPv4; |
3499 |
|
|
nc->prefix.vpn4.rd = rd->rd; |
3500 |
|
|
nc->prefix.vpn4.addr.s_addr = prefix4; |
3501 |
|
|
nc->prefix.vpn4.labellen = 3; |
3502 |
|
|
nc->prefix.vpn4.labelstack[0] = |
3503 |
|
|
(rd->label >> 12) & 0xff; |
3504 |
|
|
nc->prefix.vpn4.labelstack[1] = |
3505 |
|
|
(rd->label >> 4) & 0xff; |
3506 |
|
|
nc->prefix.vpn4.labelstack[2] = |
3507 |
|
|
(rd->label << 4) & 0xf0; |
3508 |
|
|
nc->prefix.vpn4.labelstack[2] |= BGP_MPLS_BOS; |
3509 |
|
|
break; |
3510 |
|
|
default: |
3511 |
|
|
log_warnx("unable to VPNize prefix"); |
3512 |
|
|
return; |
3513 |
|
|
} |
3514 |
|
|
} |
3515 |
|
|
} |
3516 |
|
|
|
3517 |
|
|
for (i = rib_size - 1; i > 0; i--) { |
3518 |
|
|
if (*ribs[i].name == '\0') |
3519 |
|
|
break; |
3520 |
|
|
prefix_remove(&ribs[i], peerself, &nc->prefix, nc->prefixlen, |
3521 |
|
|
flags); |
3522 |
|
|
} |
3523 |
|
|
} |
3524 |
|
|
|
3525 |
|
|
void |
3526 |
|
|
network_dump_upcall(struct rib_entry *re, void *ptr) |
3527 |
|
|
{ |
3528 |
|
|
struct prefix *p; |
3529 |
|
|
struct kroute_full k; |
3530 |
|
|
struct bgpd_addr addr; |
3531 |
|
|
struct rde_dump_ctx *ctx = ptr; |
3532 |
|
|
|
3533 |
|
|
LIST_FOREACH(p, &re->prefix_h, rib_l) { |
3534 |
|
|
if (!(p->aspath->flags & F_PREFIX_ANNOUNCED)) |
3535 |
|
|
continue; |
3536 |
|
|
pt_getaddr(p->prefix, &addr); |
3537 |
|
|
|
3538 |
|
|
bzero(&k, sizeof(k)); |
3539 |
|
|
memcpy(&k.prefix, &addr, sizeof(k.prefix)); |
3540 |
|
|
if (p->aspath->nexthop == NULL || |
3541 |
|
|
p->aspath->nexthop->state != NEXTHOP_REACH) |
3542 |
|
|
k.nexthop.aid = k.prefix.aid; |
3543 |
|
|
else |
3544 |
|
|
memcpy(&k.nexthop, &p->aspath->nexthop->true_nexthop, |
3545 |
|
|
sizeof(k.nexthop)); |
3546 |
|
|
k.prefixlen = p->prefix->prefixlen; |
3547 |
|
|
k.flags = F_KERNEL; |
3548 |
|
|
if ((p->aspath->flags & F_ANN_DYNAMIC) == 0) |
3549 |
|
|
k.flags = F_STATIC; |
3550 |
|
|
if (imsg_compose(ibuf_se_ctl, IMSG_CTL_SHOW_NETWORK, 0, |
3551 |
|
|
ctx->req.pid, -1, &k, sizeof(k)) == -1) |
3552 |
|
|
log_warnx("network_dump_upcall: " |
3553 |
|
|
"imsg_compose error"); |
3554 |
|
|
} |
3555 |
|
|
} |
3556 |
|
|
|
3557 |
|
|
/* clean up */ |
3558 |
|
|
void |
3559 |
|
|
rde_shutdown(void) |
3560 |
|
|
{ |
3561 |
|
|
struct rde_peer *p; |
3562 |
|
|
u_int32_t i; |
3563 |
|
|
|
3564 |
|
|
/* |
3565 |
|
|
* the decision process is turned off if rde_quit = 1 and |
3566 |
|
|
* rde_shutdown depends on this. |
3567 |
|
|
*/ |
3568 |
|
|
|
3569 |
|
|
/* |
3570 |
|
|
* All peers go down |
3571 |
|
|
*/ |
3572 |
|
|
for (i = 0; i <= peertable.peer_hashmask; i++) |
3573 |
|
|
while ((p = LIST_FIRST(&peertable.peer_hashtbl[i])) != NULL) |
3574 |
|
|
peer_down(p->conf.id); |
3575 |
|
|
|
3576 |
|
|
/* free filters */ |
3577 |
|
|
filterlist_free(out_rules); |
3578 |
|
|
for (i = 0; i < rib_size; i++) { |
3579 |
|
|
if (*ribs[i].name == '\0') |
3580 |
|
|
break; |
3581 |
|
|
filterlist_free(ribs[i].in_rules); |
3582 |
|
|
} |
3583 |
|
|
|
3584 |
|
|
nexthop_shutdown(); |
3585 |
|
|
path_shutdown(); |
3586 |
|
|
aspath_shutdown(); |
3587 |
|
|
attr_shutdown(); |
3588 |
|
|
pt_shutdown(); |
3589 |
|
|
peer_shutdown(); |
3590 |
|
|
} |
3591 |
|
|
|
3592 |
|
|
int |
3593 |
|
|
sa_cmp(struct bgpd_addr *a, struct sockaddr *b) |
3594 |
|
|
{ |
3595 |
|
|
struct sockaddr_in *in_b; |
3596 |
|
|
struct sockaddr_in6 *in6_b; |
3597 |
|
|
|
3598 |
|
|
if (aid2af(a->aid) != b->sa_family) |
3599 |
|
|
return (1); |
3600 |
|
|
|
3601 |
|
|
switch (b->sa_family) { |
3602 |
|
|
case AF_INET: |
3603 |
|
|
in_b = (struct sockaddr_in *)b; |
3604 |
|
|
if (a->v4.s_addr != in_b->sin_addr.s_addr) |
3605 |
|
|
return (1); |
3606 |
|
|
break; |
3607 |
|
|
case AF_INET6: |
3608 |
|
|
in6_b = (struct sockaddr_in6 *)b; |
3609 |
|
|
#ifdef __KAME__ |
3610 |
|
|
/* directly stolen from sbin/ifconfig/ifconfig.c */ |
3611 |
|
|
if (IN6_IS_ADDR_LINKLOCAL(&in6_b->sin6_addr)) { |
3612 |
|
|
in6_b->sin6_scope_id = |
3613 |
|
|
ntohs(*(u_int16_t *)&in6_b->sin6_addr.s6_addr[2]); |
3614 |
|
|
in6_b->sin6_addr.s6_addr[2] = |
3615 |
|
|
in6_b->sin6_addr.s6_addr[3] = 0; |
3616 |
|
|
} |
3617 |
|
|
#endif |
3618 |
|
|
if (bcmp(&a->v6, &in6_b->sin6_addr, |
3619 |
|
|
sizeof(struct in6_addr))) |
3620 |
|
|
return (1); |
3621 |
|
|
break; |
3622 |
|
|
default: |
3623 |
|
|
fatal("king bula sez: unknown address family"); |
3624 |
|
|
/* NOTREACHED */ |
3625 |
|
|
} |
3626 |
|
|
|
3627 |
|
|
return (0); |
3628 |
|
|
} |