]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: ISC |
8429abe0 RW |
2 | /* $OpenBSD$ */ |
3 | ||
4 | /* | |
5 | * Copyright (c) 2009 Michele Marchetto <michele@openbsd.org> | |
8429abe0 RW |
6 | */ |
7 | ||
eac6e3f0 | 8 | #include <zebra.h> |
8429abe0 RW |
9 | |
10 | #include "ldpd.h" | |
11 | #include "ldpe.h" | |
12 | #include "lde.h" | |
13 | #include "log.h" | |
eac6e3f0 | 14 | #include "ldp_debug.h" |
8429abe0 RW |
15 | |
16 | static void send_address(struct nbr *, int, struct if_addr_head *, | |
17 | unsigned int, int); | |
26519d8c RW |
18 | static int gen_address_list_tlv(struct ibuf *, int, struct if_addr_head *, |
19 | unsigned int); | |
20 | static int gen_mac_list_tlv(struct ibuf *, uint8_t *); | |
8429abe0 RW |
21 | static void address_list_add(struct if_addr_head *, struct if_addr *); |
22 | static void address_list_clr(struct if_addr_head *); | |
faf75793 RW |
23 | static void log_msg_address(int, uint16_t, struct nbr *, int, |
24 | union ldpd_addr *); | |
26519d8c | 25 | static void log_msg_mac_withdrawal(int, struct nbr *, uint8_t *); |
8429abe0 RW |
26 | |
27 | static void | |
28 | send_address(struct nbr *nbr, int af, struct if_addr_head *addr_list, | |
29 | unsigned int addr_count, int withdraw) | |
30 | { | |
31 | struct ibuf *buf; | |
32 | uint16_t msg_type; | |
33 | uint8_t addr_size; | |
34 | struct if_addr *if_addr; | |
35 | uint16_t size; | |
36 | unsigned int tlv_addr_count = 0; | |
37 | int err = 0; | |
38 | ||
39 | /* nothing to send */ | |
40 | if (LIST_EMPTY(addr_list)) | |
41 | return; | |
42 | ||
43 | if (!withdraw) | |
44 | msg_type = MSG_TYPE_ADDR; | |
45 | else | |
46 | msg_type = MSG_TYPE_ADDRWITHDRAW; | |
47 | ||
48 | switch (af) { | |
49 | case AF_INET: | |
50 | addr_size = sizeof(struct in_addr); | |
51 | break; | |
52 | case AF_INET6: | |
53 | addr_size = sizeof(struct in6_addr); | |
54 | break; | |
55 | default: | |
56 | fatalx("send_address: unknown af"); | |
57 | } | |
58 | ||
f67de3ee | 59 | while (LIST_FIRST(addr_list) != NULL) { |
8429abe0 RW |
60 | /* |
61 | * Send as many addresses as possible - respect the session's | |
62 | * negotiated maximum pdu length. | |
63 | */ | |
64 | size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE; | |
65 | if (size + addr_count * addr_size <= nbr->max_pdu_len) | |
66 | tlv_addr_count = addr_count; | |
67 | else | |
68 | tlv_addr_count = (nbr->max_pdu_len - size) / addr_size; | |
69 | size += tlv_addr_count * addr_size; | |
70 | addr_count -= tlv_addr_count; | |
71 | ||
72 | if ((buf = ibuf_open(size)) == NULL) | |
73 | fatal(__func__); | |
74 | ||
ee60199a | 75 | SET_FLAG(err, gen_ldp_hdr(buf, size)); |
8429abe0 | 76 | size -= LDP_HDR_SIZE; |
ee60199a | 77 | SET_FLAG(err, gen_msg_hdr(buf, msg_type, size)); |
8429abe0 | 78 | size -= LDP_MSG_SIZE; |
ee60199a | 79 | SET_FLAG(err, gen_address_list_tlv(buf, af, addr_list, tlv_addr_count)); |
e3df3ba6 | 80 | (void)size; |
ee60199a | 81 | |
8429abe0 RW |
82 | if (err) { |
83 | address_list_clr(addr_list); | |
84 | ibuf_free(buf); | |
85 | return; | |
86 | } | |
87 | ||
88 | while ((if_addr = LIST_FIRST(addr_list)) != NULL) { | |
faf75793 | 89 | log_msg_address(1, msg_type, nbr, af, &if_addr->addr); |
8429abe0 RW |
90 | |
91 | LIST_REMOVE(if_addr, entry); | |
11bf8e13 | 92 | assert(if_addr != LIST_FIRST(addr_list)); |
8429abe0 RW |
93 | free(if_addr); |
94 | if (--tlv_addr_count == 0) | |
95 | break; | |
96 | } | |
97 | ||
98 | evbuf_enqueue(&nbr->tcp->wbuf, buf); | |
0f7b5df9 RW |
99 | |
100 | /* no errors - update per neighbor message counters */ | |
101 | switch (msg_type) { | |
102 | case MSG_TYPE_ADDR: | |
103 | nbr->stats.addr_sent++; | |
104 | break; | |
105 | case MSG_TYPE_ADDRWITHDRAW: | |
106 | nbr->stats.addrwdraw_sent++; | |
107 | break; | |
108 | default: | |
109 | break; | |
110 | } | |
8429abe0 RW |
111 | } |
112 | ||
113 | nbr_fsm(nbr, NBR_EVT_PDU_SENT); | |
114 | } | |
115 | ||
116 | void | |
117 | send_address_single(struct nbr *nbr, struct if_addr *if_addr, int withdraw) | |
118 | { | |
119 | struct if_addr_head addr_list; | |
120 | ||
121 | LIST_INIT(&addr_list); | |
122 | address_list_add(&addr_list, if_addr); | |
123 | send_address(nbr, if_addr->af, &addr_list, 1, withdraw); | |
124 | } | |
125 | ||
126 | void | |
127 | send_address_all(struct nbr *nbr, int af) | |
128 | { | |
129 | struct if_addr_head addr_list; | |
130 | struct if_addr *if_addr; | |
131 | unsigned int addr_count = 0; | |
132 | ||
133 | LIST_INIT(&addr_list); | |
134 | LIST_FOREACH(if_addr, &global.addr_list, entry) { | |
135 | if (if_addr->af != af) | |
136 | continue; | |
137 | ||
138 | address_list_add(&addr_list, if_addr); | |
139 | addr_count++; | |
140 | } | |
141 | ||
142 | send_address(nbr, af, &addr_list, addr_count, 0); | |
143 | } | |
144 | ||
26519d8c RW |
145 | void |
146 | send_mac_withdrawal(struct nbr *nbr, struct map *fec, uint8_t *mac) | |
147 | { | |
148 | struct ibuf *buf; | |
149 | uint16_t size; | |
150 | int err; | |
151 | ||
152 | size = LDP_HDR_SIZE + LDP_MSG_SIZE + ADDR_LIST_SIZE + len_fec_tlv(fec) + | |
153 | TLV_HDR_SIZE; | |
154 | if (mac) | |
9bff8057 | 155 | size += ETH_ALEN; |
26519d8c RW |
156 | |
157 | if ((buf = ibuf_open(size)) == NULL) | |
158 | fatal(__func__); | |
159 | ||
160 | err = gen_ldp_hdr(buf, size); | |
161 | size -= LDP_HDR_SIZE; | |
ee60199a | 162 | |
163 | SET_FLAG(err, gen_msg_hdr(buf, MSG_TYPE_ADDRWITHDRAW, size)); | |
164 | SET_FLAG(err, gen_address_list_tlv(buf, AF_INET, NULL, 0)); | |
165 | SET_FLAG(err, gen_fec_tlv(buf, fec)); | |
166 | SET_FLAG(err, gen_mac_list_tlv(buf, mac)); | |
167 | ||
26519d8c RW |
168 | if (err) { |
169 | ibuf_free(buf); | |
170 | return; | |
171 | } | |
172 | ||
173 | log_msg_mac_withdrawal(1, nbr, mac); | |
174 | ||
175 | evbuf_enqueue(&nbr->tcp->wbuf, buf); | |
176 | ||
177 | nbr_fsm(nbr, NBR_EVT_PDU_SENT); | |
178 | } | |
179 | ||
8429abe0 RW |
180 | int |
181 | recv_address(struct nbr *nbr, char *buf, uint16_t len) | |
182 | { | |
183 | struct ldp_msg msg; | |
184 | uint16_t msg_type; | |
8429abe0 | 185 | enum imsg_type type; |
fd1cf443 RW |
186 | struct address_list_tlv alt; |
187 | uint16_t alt_len; | |
188 | uint16_t alt_family; | |
8429abe0 RW |
189 | struct lde_addr lde_addr; |
190 | ||
191 | memcpy(&msg, buf, sizeof(msg)); | |
fd1cf443 RW |
192 | msg_type = ntohs(msg.type); |
193 | switch (msg_type) { | |
194 | case MSG_TYPE_ADDR: | |
195 | type = IMSG_ADDRESS_ADD; | |
196 | break; | |
197 | case MSG_TYPE_ADDRWITHDRAW: | |
198 | type = IMSG_ADDRESS_DEL; | |
199 | break; | |
200 | default: | |
201 | fatalx("recv_address: unexpected msg type"); | |
202 | } | |
8429abe0 RW |
203 | buf += LDP_MSG_SIZE; |
204 | len -= LDP_MSG_SIZE; | |
205 | ||
206 | /* Address List TLV */ | |
207 | if (len < ADDR_LIST_SIZE) { | |
208 | session_shutdown(nbr, S_BAD_MSG_LEN, msg.id, msg.type); | |
209 | return (-1); | |
210 | } | |
8429abe0 | 211 | memcpy(&alt, buf, sizeof(alt)); |
fd1cf443 RW |
212 | alt_len = ntohs(alt.length); |
213 | alt_family = ntohs(alt.family); | |
214 | if (alt_len > len - TLV_HDR_SIZE) { | |
8429abe0 RW |
215 | session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); |
216 | return (-1); | |
217 | } | |
218 | if (ntohs(alt.type) != TLV_TYPE_ADDRLIST) { | |
a33df200 | 219 | send_notification(nbr->tcp, S_MISS_MSG, msg.id, msg.type); |
8429abe0 RW |
220 | return (-1); |
221 | } | |
fd1cf443 | 222 | switch (alt_family) { |
8429abe0 RW |
223 | case AF_IPV4: |
224 | if (!nbr->v4_enabled) | |
225 | /* just ignore the message */ | |
226 | return (0); | |
227 | break; | |
228 | case AF_IPV6: | |
229 | if (!nbr->v6_enabled) | |
230 | /* just ignore the message */ | |
231 | return (0); | |
232 | break; | |
233 | default: | |
adbdf465 | 234 | send_notification(nbr->tcp, S_UNSUP_ADDR, msg.id, msg.type); |
8429abe0 RW |
235 | return (-1); |
236 | } | |
fd1cf443 | 237 | alt_len -= sizeof(alt.family); |
8429abe0 RW |
238 | buf += sizeof(alt); |
239 | len -= sizeof(alt); | |
240 | ||
fd1cf443 RW |
241 | /* Process all received addresses */ |
242 | while (alt_len > 0) { | |
243 | switch (alt_family) { | |
8429abe0 | 244 | case AF_IPV4: |
fd1cf443 | 245 | if (alt_len < sizeof(struct in_addr)) { |
8429abe0 RW |
246 | session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, |
247 | msg.type); | |
248 | return (-1); | |
249 | } | |
250 | ||
251 | memset(&lde_addr, 0, sizeof(lde_addr)); | |
252 | lde_addr.af = AF_INET; | |
253 | memcpy(&lde_addr.addr, buf, sizeof(struct in_addr)); | |
254 | ||
255 | buf += sizeof(struct in_addr); | |
256 | len -= sizeof(struct in_addr); | |
fd1cf443 | 257 | alt_len -= sizeof(struct in_addr); |
8429abe0 RW |
258 | break; |
259 | case AF_IPV6: | |
fd1cf443 | 260 | if (alt_len < sizeof(struct in6_addr)) { |
8429abe0 RW |
261 | session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, |
262 | msg.type); | |
263 | return (-1); | |
264 | } | |
265 | ||
266 | memset(&lde_addr, 0, sizeof(lde_addr)); | |
267 | lde_addr.af = AF_INET6; | |
268 | memcpy(&lde_addr.addr, buf, sizeof(struct in6_addr)); | |
269 | ||
270 | buf += sizeof(struct in6_addr); | |
271 | len -= sizeof(struct in6_addr); | |
fd1cf443 | 272 | alt_len -= sizeof(struct in6_addr); |
8429abe0 RW |
273 | break; |
274 | default: | |
275 | fatalx("recv_address: unknown af"); | |
276 | } | |
277 | ||
faf75793 | 278 | log_msg_address(0, msg_type, nbr, lde_addr.af, &lde_addr.addr); |
8429abe0 RW |
279 | |
280 | ldpe_imsg_compose_lde(type, nbr->peerid, 0, &lde_addr, | |
281 | sizeof(lde_addr)); | |
282 | } | |
283 | ||
fd1cf443 RW |
284 | /* Optional Parameters */ |
285 | while (len > 0) { | |
286 | struct tlv tlv; | |
287 | uint16_t tlv_type; | |
288 | uint16_t tlv_len; | |
289 | ||
290 | if (len < sizeof(tlv)) { | |
291 | session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); | |
292 | return (-1); | |
293 | } | |
294 | ||
295 | memcpy(&tlv, buf, TLV_HDR_SIZE); | |
296 | tlv_type = ntohs(tlv.type); | |
297 | tlv_len = ntohs(tlv.length); | |
298 | if (tlv_len + TLV_HDR_SIZE > len) { | |
299 | session_shutdown(nbr, S_BAD_TLV_LEN, msg.id, msg.type); | |
300 | return (-1); | |
301 | } | |
302 | buf += TLV_HDR_SIZE; | |
303 | len -= TLV_HDR_SIZE; | |
304 | ||
305 | switch (tlv_type) { | |
306 | default: | |
307 | if (!(ntohs(tlv.type) & UNKNOWN_FLAG)) | |
308 | send_notification_rtlvs(nbr, S_UNKNOWN_TLV, | |
309 | msg.id, msg.type, tlv_type, tlv_len, buf); | |
310 | /* ignore unknown tlv */ | |
311 | break; | |
312 | } | |
313 | buf += tlv_len; | |
314 | len -= tlv_len; | |
315 | } | |
316 | ||
8429abe0 RW |
317 | return (0); |
318 | } | |
319 | ||
320 | static int | |
26519d8c RW |
321 | gen_address_list_tlv(struct ibuf *buf, int af, struct if_addr_head *addr_list, |
322 | unsigned int tlv_addr_count) | |
8429abe0 RW |
323 | { |
324 | struct address_list_tlv alt; | |
325 | uint16_t addr_size; | |
326 | struct if_addr *if_addr; | |
327 | int err = 0; | |
328 | ||
329 | memset(&alt, 0, sizeof(alt)); | |
a33df200 | 330 | alt.type = htons(TLV_TYPE_ADDRLIST); |
8429abe0 RW |
331 | |
332 | switch (af) { | |
333 | case AF_INET: | |
334 | alt.family = htons(AF_IPV4); | |
335 | addr_size = sizeof(struct in_addr); | |
336 | break; | |
337 | case AF_INET6: | |
338 | alt.family = htons(AF_IPV6); | |
339 | addr_size = sizeof(struct in6_addr); | |
340 | break; | |
341 | default: | |
342 | fatalx("gen_address_list_tlv: unknown af"); | |
343 | } | |
26519d8c | 344 | alt.length = htons(sizeof(alt.family) + addr_size * tlv_addr_count); |
8429abe0 | 345 | |
ee60199a | 346 | SET_FLAG(err, ibuf_add(buf, &alt, sizeof(alt))); |
347 | ||
26519d8c RW |
348 | if (addr_list == NULL) |
349 | return (err); | |
350 | ||
8429abe0 | 351 | LIST_FOREACH(if_addr, addr_list, entry) { |
ee60199a | 352 | SET_FLAG(err, ibuf_add(buf, &if_addr->addr, addr_size)); |
353 | ||
8429abe0 RW |
354 | if (--tlv_addr_count == 0) |
355 | break; | |
356 | } | |
357 | ||
358 | return (err); | |
359 | } | |
360 | ||
26519d8c RW |
361 | static int |
362 | gen_mac_list_tlv(struct ibuf *buf, uint8_t *mac) | |
363 | { | |
364 | struct tlv tlv; | |
365 | int err; | |
366 | ||
367 | memset(&tlv, 0, sizeof(tlv)); | |
368 | tlv.type = htons(TLV_TYPE_MAC_LIST); | |
369 | if (mac) | |
9bff8057 | 370 | tlv.length = htons(ETH_ALEN); |
26519d8c RW |
371 | err = ibuf_add(buf, &tlv, sizeof(tlv)); |
372 | if (mac) | |
ee60199a | 373 | SET_FLAG(err, ibuf_add(buf, mac, ETH_ALEN)); |
26519d8c RW |
374 | |
375 | return (err); | |
376 | } | |
377 | ||
8429abe0 RW |
378 | static void |
379 | address_list_add(struct if_addr_head *addr_list, struct if_addr *if_addr) | |
380 | { | |
381 | struct if_addr *new; | |
382 | ||
383 | new = malloc(sizeof(*new)); | |
384 | if (new == NULL) | |
385 | fatal(__func__); | |
386 | *new = *if_addr; | |
387 | ||
388 | LIST_INSERT_HEAD(addr_list, new, entry); | |
389 | } | |
390 | ||
391 | static void | |
392 | address_list_clr(struct if_addr_head *addr_list) | |
393 | { | |
394 | struct if_addr *if_addr; | |
395 | ||
396 | while ((if_addr = LIST_FIRST(addr_list)) != NULL) { | |
397 | LIST_REMOVE(if_addr, entry); | |
11bf8e13 | 398 | assert(if_addr != LIST_FIRST(addr_list)); |
8429abe0 RW |
399 | free(if_addr); |
400 | } | |
401 | } | |
faf75793 RW |
402 | |
403 | static void | |
404 | log_msg_address(int out, uint16_t msg_type, struct nbr *nbr, int af, | |
405 | union ldpd_addr *addr) | |
406 | { | |
903a7226 MS |
407 | debug_msg(out, "%s: lsr-id %pI4, address %s", msg_name(msg_type), |
408 | &nbr->id, log_addr(af, addr)); | |
faf75793 | 409 | } |
26519d8c RW |
410 | |
411 | static void | |
412 | log_msg_mac_withdrawal(int out, struct nbr *nbr, uint8_t *mac) | |
413 | { | |
414 | char buf[ETHER_ADDR_STRLEN]; | |
415 | ||
903a7226 | 416 | debug_msg(out, "mac withdrawal: lsr-id %pI4, mac %s", &nbr->id, |
26519d8c RW |
417 | (mac) ? prefix_mac2str((struct ethaddr *)mac, buf, sizeof(buf)) : |
418 | "wildcard"); | |
419 | } |