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