]>
Commit | Line | Data |
---|---|---|
c01e3741 MM |
1 | /* |
2 | * BIRD -- BGP Packet Processing | |
3 | * | |
4 | * (c) 2000 Martin Mares <mj@ucw.cz> | |
5 | * | |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
85368cd4 | 9 | #undef LOCAL_DEBUG |
72a6ef11 | 10 | |
c01e3741 MM |
11 | #include "nest/bird.h" |
12 | #include "nest/iface.h" | |
13 | #include "nest/protocol.h" | |
14 | #include "nest/route.h" | |
15 | #include "conf/conf.h" | |
72a6ef11 MM |
16 | #include "lib/unaligned.h" |
17 | #include "lib/socket.h" | |
c01e3741 MM |
18 | |
19 | #include "bgp.h" | |
72a6ef11 MM |
20 | |
21 | static byte * | |
22 | bgp_create_notification(struct bgp_conn *conn, byte *buf) | |
23 | { | |
85368cd4 MM |
24 | struct bgp_proto *p = conn->bgp; |
25 | ||
26 | BGP_TRACE(D_PACKETS, "Sending NOTIFICATION(code=%d.%d)", conn->notify_code, conn->notify_subcode); | |
72a6ef11 MM |
27 | buf[0] = conn->notify_code; |
28 | buf[1] = conn->notify_subcode; | |
efcece2d MM |
29 | memcpy(buf+2, conn->notify_data, conn->notify_size); |
30 | return buf + 2 + conn->notify_size; | |
72a6ef11 MM |
31 | } |
32 | ||
33 | static byte * | |
34 | bgp_create_open(struct bgp_conn *conn, byte *buf) | |
35 | { | |
85368cd4 MM |
36 | struct bgp_proto *p = conn->bgp; |
37 | ||
38 | BGP_TRACE(D_PACKETS, "Sending OPEN(ver=%d,as=%d,hold=%d,id=%08x)", | |
39 | BGP_VERSION, p->local_as, p->cf->hold_time, p->local_id); | |
72a6ef11 | 40 | buf[0] = BGP_VERSION; |
85368cd4 MM |
41 | put_u16(buf+1, p->local_as); |
42 | put_u16(buf+3, p->cf->hold_time); | |
43 | put_u32(buf+5, p->local_id); | |
72a6ef11 MM |
44 | buf[9] = 0; /* No optional parameters */ |
45 | return buf+10; | |
46 | } | |
47 | ||
f421cfdd MM |
48 | static unsigned int |
49 | bgp_encode_prefixes(struct bgp_proto *p, byte *w, struct bgp_bucket *buck, unsigned int remains) | |
50 | { | |
51 | byte *start = w; | |
52 | ip_addr a; | |
53 | int bytes; | |
54 | ||
55 | while (!EMPTY_LIST(buck->prefixes) && remains >= 5) | |
56 | { | |
57 | struct bgp_prefix *px = SKIP_BACK(struct bgp_prefix, bucket_node, HEAD(buck->prefixes)); | |
58 | DBG("\tDequeued route %I/%d\n", px->n.prefix, px->n.pxlen); | |
59 | *w++ = px->n.pxlen; | |
60 | bytes = (px->n.pxlen + 7) / 8; | |
61 | a = px->n.prefix; | |
62 | ipa_hton(a); | |
63 | memcpy(w, &a, bytes); | |
64 | w += bytes; | |
65 | rem_node(&px->bucket_node); | |
66 | fib_delete(&p->prefix_fib, px); | |
67 | } | |
68 | return w - start; | |
69 | } | |
70 | ||
1c1da87b MM |
71 | #ifndef IPV6 /* IPv4 version */ |
72 | ||
72a6ef11 MM |
73 | static byte * |
74 | bgp_create_update(struct bgp_conn *conn, byte *buf) | |
75 | { | |
85368cd4 | 76 | struct bgp_proto *p = conn->bgp; |
f421cfdd MM |
77 | struct bgp_bucket *buck; |
78 | int remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - 4; | |
79 | byte *w, *wold; | |
80 | ip_addr ip; | |
81 | int wd_size = 0; | |
82 | int r_size = 0; | |
83 | ||
85368cd4 | 84 | BGP_TRACE(D_PACKETS, "Sending UPDATE"); |
f421cfdd | 85 | w = buf+2; |
85368cd4 | 86 | if ((buck = p->withdraw_bucket) && !EMPTY_LIST(buck->prefixes)) |
f421cfdd MM |
87 | { |
88 | DBG("Withdrawn routes:\n"); | |
85368cd4 | 89 | wd_size = bgp_encode_prefixes(p, w, buck, remains); |
f421cfdd MM |
90 | w += wd_size; |
91 | remains -= wd_size; | |
92 | } | |
93 | put_u16(buf, wd_size); | |
3fdbafb6 | 94 | |
f421cfdd MM |
95 | if (remains >= 2048) |
96 | { | |
85368cd4 | 97 | while ((buck = (struct bgp_bucket *) HEAD(p->bucket_queue))->send_node.next) |
f421cfdd MM |
98 | { |
99 | if (EMPTY_LIST(buck->prefixes)) | |
100 | { | |
101 | DBG("Deleting empty bucket %p\n", buck); | |
102 | rem_node(&buck->send_node); | |
85368cd4 | 103 | bgp_free_bucket(p, buck); |
f421cfdd MM |
104 | continue; |
105 | } | |
106 | DBG("Processing bucket %p\n", buck); | |
107 | w = bgp_encode_attrs(w, buck); | |
108 | remains = BGP_MAX_PACKET_LENGTH - BGP_HEADER_LENGTH - (w-buf); | |
85368cd4 | 109 | r_size = bgp_encode_prefixes(p, w, buck, remains); |
f421cfdd MM |
110 | w += r_size; |
111 | break; | |
112 | } | |
113 | } | |
114 | else | |
115 | { | |
116 | put_u16(w, 0); | |
117 | w += 2; | |
118 | } | |
119 | return (wd_size || r_size) ? w : NULL; | |
72a6ef11 MM |
120 | } |
121 | ||
1c1da87b MM |
122 | #else /* IPv6 version */ |
123 | ||
124 | static byte * | |
125 | bgp_create_update(struct bgp_conn *conn, byte *buf) | |
126 | { | |
127 | return NULL; | |
128 | } | |
129 | ||
130 | #endif | |
131 | ||
72a6ef11 MM |
132 | static void |
133 | bgp_create_header(byte *buf, unsigned int len, unsigned int type) | |
134 | { | |
135 | memset(buf, 0xff, 16); /* Marker */ | |
136 | put_u16(buf+16, len); | |
137 | buf[18] = type; | |
138 | } | |
139 | ||
3fdbafb6 | 140 | static int |
72a6ef11 MM |
141 | bgp_fire_tx(struct bgp_conn *conn) |
142 | { | |
85368cd4 | 143 | struct bgp_proto *p = conn->bgp; |
72a6ef11 MM |
144 | unsigned int s = conn->packets_to_send; |
145 | sock *sk = conn->sk; | |
146 | byte *buf = sk->tbuf; | |
147 | byte *pkt = buf + BGP_HEADER_LENGTH; | |
148 | byte *end; | |
149 | int type; | |
150 | ||
151 | if (s & (1 << PKT_SCHEDULE_CLOSE)) | |
152 | { | |
3fdbafb6 MM |
153 | bgp_close_conn(conn); |
154 | return 0; | |
72a6ef11 MM |
155 | } |
156 | if (s & (1 << PKT_NOTIFICATION)) | |
157 | { | |
158 | s = 1 << PKT_SCHEDULE_CLOSE; | |
159 | type = PKT_NOTIFICATION; | |
160 | end = bgp_create_notification(conn, pkt); | |
161 | } | |
162 | else if (s & (1 << PKT_KEEPALIVE)) | |
163 | { | |
164 | s &= ~(1 << PKT_KEEPALIVE); | |
165 | type = PKT_KEEPALIVE; | |
166 | end = pkt; /* Keepalives carry no data */ | |
85368cd4 | 167 | BGP_TRACE(D_PACKETS, "Sending KEEPALIVE"); |
3fdbafb6 | 168 | bgp_start_timer(conn->keepalive_timer, conn->keepalive_time); |
72a6ef11 MM |
169 | } |
170 | else if (s & (1 << PKT_OPEN)) | |
171 | { | |
172 | s &= ~(1 << PKT_OPEN); | |
173 | type = PKT_OPEN; | |
174 | end = bgp_create_open(conn, pkt); | |
175 | } | |
176 | else if (s & (1 << PKT_UPDATE)) | |
177 | { | |
178 | end = bgp_create_update(conn, pkt); | |
179 | type = PKT_UPDATE; | |
180 | if (!end) | |
181 | { | |
182 | conn->packets_to_send = 0; | |
3fdbafb6 | 183 | return 0; |
72a6ef11 MM |
184 | } |
185 | } | |
186 | else | |
3fdbafb6 | 187 | return 0; |
72a6ef11 MM |
188 | conn->packets_to_send = s; |
189 | bgp_create_header(buf, end - buf, type); | |
3fdbafb6 | 190 | return sk_send(sk, end - buf); |
72a6ef11 MM |
191 | } |
192 | ||
193 | void | |
194 | bgp_schedule_packet(struct bgp_conn *conn, int type) | |
195 | { | |
196 | DBG("BGP: Scheduling packet type %d\n", type); | |
197 | conn->packets_to_send |= 1 << type; | |
3fdbafb6 MM |
198 | if (conn->sk->tpos == conn->sk->tbuf) |
199 | while (bgp_fire_tx(conn)) | |
200 | ; | |
72a6ef11 MM |
201 | } |
202 | ||
203 | void | |
204 | bgp_tx(sock *sk) | |
205 | { | |
206 | struct bgp_conn *conn = sk->data; | |
207 | ||
208 | DBG("BGP: TX hook\n"); | |
3fdbafb6 MM |
209 | while (bgp_fire_tx(conn)) |
210 | ; | |
211 | } | |
212 | ||
a47a0108 MM |
213 | static int |
214 | bgp_parse_options(struct bgp_conn *conn, byte *opt, int len) | |
215 | { | |
216 | while (len > 0) | |
217 | { | |
218 | if (len < 2 || len < 2 + opt[1]) | |
efcece2d | 219 | { bgp_error(conn, 2, 0, NULL, 0); return 0; } |
a47a0108 MM |
220 | #ifdef LOCAL_DEBUG |
221 | { | |
222 | int i; | |
223 | DBG("\tOption %02x:", opt[0]); | |
224 | for(i=0; i<opt[1]; i++) | |
225 | DBG(" %02x", opt[2+i]); | |
226 | DBG("\n"); | |
227 | } | |
228 | #endif | |
229 | switch (opt[0]) | |
230 | { | |
231 | case 2: | |
232 | /* Defined in draft-ietf-idr-bgp4-cap-neg-06 */ | |
233 | /* We can safely ignore all capabilities */ | |
234 | break; | |
235 | default: | |
236 | /* | |
237 | * BGP specs don't tell us to send which option | |
238 | * we didn't recognize, but it's common practice | |
239 | * to do so. Also, capability negotiation with | |
240 | * Cisco routers doesn't work without that. | |
241 | */ | |
efcece2d | 242 | bgp_error(conn, 2, 4, opt, opt[1]); |
a47a0108 MM |
243 | return 0; |
244 | } | |
245 | len -= 2 + opt[1]; | |
246 | opt += 2 + opt[1]; | |
247 | } | |
248 | return 0; | |
249 | } | |
250 | ||
3fdbafb6 MM |
251 | static void |
252 | bgp_rx_open(struct bgp_conn *conn, byte *pkt, int len) | |
253 | { | |
b552ecc4 | 254 | struct bgp_conn *other; |
3fdbafb6 MM |
255 | struct bgp_proto *p = conn->bgp; |
256 | struct bgp_config *cf = p->cf; | |
257 | unsigned as, hold; | |
258 | u32 id; | |
259 | ||
260 | /* Check state */ | |
261 | if (conn->state != BS_OPENSENT) | |
efcece2d | 262 | { bgp_error(conn, 5, 0, NULL, 0); } |
3fdbafb6 MM |
263 | |
264 | /* Check message contents */ | |
265 | if (len < 29 || len != 29 + pkt[28]) | |
efcece2d | 266 | { bgp_error(conn, 1, 2, pkt+16, 2); return; } |
3fdbafb6 | 267 | if (pkt[19] != BGP_VERSION) |
efcece2d | 268 | { bgp_error(conn, 2, 1, pkt+19, 1); return; } /* RFC 1771 says 16 bits, draft-09 tells to use 8 */ |
3fdbafb6 MM |
269 | as = get_u16(pkt+20); |
270 | hold = get_u16(pkt+22); | |
271 | id = get_u32(pkt+24); | |
85368cd4 | 272 | BGP_TRACE(D_PACKETS, "Got OPEN(as=%d,hold=%d,id=%08x)", as, hold, id); |
3fdbafb6 | 273 | if (cf->remote_as && as != p->remote_as) |
efcece2d | 274 | { bgp_error(conn, 2, 2, pkt+20, -2); return; } |
3fdbafb6 | 275 | if (hold > 0 && hold < 3) |
efcece2d | 276 | { bgp_error(conn, 2, 6, pkt+22, 2); return; } |
3fdbafb6 | 277 | p->remote_id = id; |
a47a0108 MM |
278 | if (bgp_parse_options(conn, pkt+29, pkt[28])) |
279 | return; | |
3fdbafb6 | 280 | if (!id || id == 0xffffffff || id == p->local_id) |
efcece2d | 281 | { bgp_error(conn, 2, 3, pkt+24, -4); return; } |
3fdbafb6 | 282 | |
b552ecc4 MM |
283 | /* Check the other connection */ |
284 | other = (conn == &p->outgoing_conn) ? &p->incoming_conn : &p->outgoing_conn; | |
285 | switch (other->state) | |
286 | { | |
287 | case BS_IDLE: | |
288 | break; | |
289 | case BS_CONNECT: | |
290 | case BS_ACTIVE: | |
291 | case BS_OPENSENT: | |
85368cd4 | 292 | BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection"); |
b552ecc4 MM |
293 | bgp_close_conn(other); |
294 | break; | |
295 | case BS_OPENCONFIRM: | |
296 | if ((p->local_id < id) == (conn == &p->incoming_conn)) | |
297 | { | |
298 | /* Should close the other connection */ | |
85368cd4 | 299 | BGP_TRACE(D_EVENTS, "Connection collision, giving up the other connection"); |
efcece2d | 300 | bgp_error(other, 6, 0, NULL, 0); |
b552ecc4 MM |
301 | break; |
302 | } | |
303 | /* Fall thru */ | |
304 | case BS_ESTABLISHED: | |
305 | /* Should close this connection */ | |
85368cd4 | 306 | BGP_TRACE(D_EVENTS, "Connection collision, giving up this connection"); |
efcece2d | 307 | bgp_error(conn, 6, 0, NULL, 0); |
b552ecc4 MM |
308 | return; |
309 | default: | |
310 | bug("bgp_rx_open: Unknown state"); | |
311 | } | |
312 | ||
313 | /* Make this connection primary */ | |
314 | conn->primary = 1; | |
315 | p->conn = conn; | |
3fdbafb6 MM |
316 | |
317 | /* Update our local variables */ | |
318 | if (hold < p->cf->hold_time) | |
319 | conn->hold_time = hold; | |
320 | else | |
321 | conn->hold_time = p->cf->hold_time; | |
322 | conn->keepalive_time = p->cf->keepalive_time ? : conn->hold_time / 3; | |
323 | p->remote_as = as; | |
324 | p->remote_id = id; | |
325 | DBG("BGP: Hold timer set to %d, keepalive to %d, AS to %d, ID to %x\n", conn->hold_time, conn->keepalive_time, p->remote_as, p->remote_id); | |
326 | ||
327 | bgp_schedule_packet(conn, PKT_KEEPALIVE); | |
328 | bgp_start_timer(conn->hold_timer, conn->hold_time); | |
329 | conn->state = BS_OPENCONFIRM; | |
330 | } | |
331 | ||
973399ae MM |
332 | #define DECODE_PREFIX(pp, ll) do { \ |
333 | int b = *pp++; \ | |
334 | int q; \ | |
973399ae | 335 | ll--; \ |
1c1da87b | 336 | if (b > BITS_PER_IP_ADDRESS) { err=10; goto bad; } \ |
973399ae | 337 | q = (b+7) / 8; \ |
1c1da87b | 338 | if (ll < q) { err=1; goto bad; } \ |
c00d31be | 339 | memcpy(&prefix, pp, q); \ |
973399ae MM |
340 | pp += q; \ |
341 | ll -= q; \ | |
f421cfdd MM |
342 | ipa_ntoh(prefix); \ |
343 | prefix = ipa_and(prefix, ipa_mkmask(b)); \ | |
c00d31be | 344 | pxlen = b; \ |
973399ae MM |
345 | } while (0) |
346 | ||
1c1da87b MM |
347 | static inline int |
348 | bgp_get_nexthop(struct bgp_proto *bgp, rta *a) | |
349 | { | |
350 | neighbor *neigh; | |
351 | ip_addr nexthop; | |
352 | struct eattr *nh = ea_find(a->eattrs, EA_CODE(EAP_BGP, BA_NEXT_HOP)); | |
353 | ASSERT(nh); | |
354 | nexthop = *(ip_addr *) nh->u.ptr->data; | |
355 | if (ipa_equal(nexthop, bgp->local_addr)) | |
356 | { | |
357 | DBG("BGP: Loop!\n"); | |
358 | return 0; | |
359 | } | |
360 | neigh = neigh_find(&bgp->p, &nexthop, 0) ? : bgp->neigh; | |
361 | a->gw = neigh->addr; | |
362 | a->iface = neigh->iface; | |
363 | return 1; | |
364 | } | |
365 | ||
366 | #ifndef IPV6 /* IPv4 version */ | |
367 | ||
3fdbafb6 | 368 | static void |
1c1da87b MM |
369 | bgp_do_rx_update(struct bgp_conn *conn, |
370 | byte *withdrawn, int withdrawn_len, | |
371 | byte *nlri, int nlri_len, | |
372 | byte *attrs, int attr_len) | |
3fdbafb6 | 373 | { |
85368cd4 | 374 | struct bgp_proto *p = conn->bgp; |
1c1da87b MM |
375 | rta *a0; |
376 | rta *a = NULL; | |
c00d31be | 377 | ip_addr prefix; |
c00d31be | 378 | net *n; |
973399ae | 379 | rte e; |
1c1da87b | 380 | int err = 0, pxlen; |
973399ae MM |
381 | |
382 | /* Withdraw routes */ | |
383 | while (withdrawn_len) | |
384 | { | |
385 | DECODE_PREFIX(withdrawn, withdrawn_len); | |
c00d31be | 386 | DBG("Withdraw %I/%d\n", prefix, pxlen); |
85368cd4 MM |
387 | if (n = net_find(p->p.table, prefix, pxlen)) |
388 | rte_update(p->p.table, n, &p->p, NULL); | |
973399ae MM |
389 | } |
390 | ||
f94557de MM |
391 | if (!attr_len && !nlri_len) /* shortcut */ |
392 | return; | |
393 | ||
2a9e064d | 394 | a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, nlri_len); |
1c1da87b | 395 | if (a0 && nlri_len && bgp_get_nexthop(p, a0)) |
973399ae | 396 | { |
2a9e064d | 397 | a = rta_lookup(a0); |
c00d31be | 398 | while (nlri_len) |
973399ae | 399 | { |
c00d31be | 400 | rte *e; |
f8809249 | 401 | DECODE_PREFIX(nlri, nlri_len); |
c00d31be | 402 | DBG("Add %I/%d\n", prefix, pxlen); |
f8809249 | 403 | e = rte_get_temp(rta_clone(a)); |
85368cd4 | 404 | n = net_get(p->p.table, prefix, pxlen); |
c00d31be MM |
405 | e->net = n; |
406 | e->pflags = 0; | |
85368cd4 | 407 | rte_update(p->p.table, n, &p->p, e); |
973399ae | 408 | } |
973399ae | 409 | } |
1c1da87b MM |
410 | bad: |
411 | if (a) | |
412 | rta_free(a); | |
413 | if (err) | |
414 | bgp_error(conn, 3, err, NULL, 0); | |
f8809249 | 415 | return; |
1c1da87b | 416 | } |
f8809249 | 417 | |
1c1da87b MM |
418 | #else /* IPv6 version */ |
419 | ||
420 | #define DO_NLRI(name) \ | |
421 | start = x = p->name##_start; \ | |
422 | len = len0 = p->name##_len; \ | |
423 | if (len) \ | |
424 | { \ | |
425 | if (len < 3) goto bad; \ | |
426 | af = get_u16(x); \ | |
427 | sub = x[2]; \ | |
428 | x += 3; \ | |
429 | len -= 3; \ | |
430 | DBG("\tNLRI AF=%d sub=%d len=%d\n", af, sub, len);\ | |
431 | } \ | |
432 | else \ | |
433 | af = 0; \ | |
434 | if (af == BGP_AF_IPV6) | |
435 | ||
436 | static void | |
437 | bgp_do_rx_update(struct bgp_conn *conn, | |
438 | byte *withdrawn, int withdrawn_len, | |
439 | byte *nlri, int nlri_len, | |
440 | byte *attrs, int attr_len) | |
441 | { | |
442 | struct bgp_proto *p = conn->bgp; | |
443 | byte *start, *x; | |
444 | int len, len0; | |
445 | unsigned af, sub; | |
446 | rta *a0; | |
447 | rta *a = NULL; | |
448 | ip_addr prefix; | |
449 | net *n; | |
450 | rte e; | |
451 | int err = 0, pxlen; | |
452 | ||
453 | p->mp_reach_len = 0; | |
454 | p->mp_unreach_len = 0; | |
455 | a0 = bgp_decode_attrs(conn, attrs, attr_len, bgp_linpool, 0); | |
456 | if (!a0) | |
457 | return; | |
458 | ||
459 | DO_NLRI(mp_unreach) | |
460 | { | |
461 | while (len) | |
462 | { | |
463 | DECODE_PREFIX(x, len); | |
464 | DBG("Withdraw %I/%d\n", prefix, pxlen); | |
465 | if (n = net_find(p->p.table, prefix, pxlen)) | |
466 | rte_update(p->p.table, n, &p->p, NULL); | |
467 | } | |
468 | } | |
469 | ||
470 | DO_NLRI(mp_reach) | |
471 | { | |
472 | ea_list *e = lp_alloc(bgp_linpool, sizeof(ea_list) + sizeof(eattr)); | |
473 | struct adata *ad = lp_alloc(bgp_linpool, sizeof(struct adata) + 16); | |
474 | int i; | |
475 | ||
476 | /* Create fake NEXT_HOP attribute */ | |
477 | if (len < 1 || (*x != 16 && *x != 32) || len < *x + 2) | |
478 | goto bad; | |
479 | e->next = a0->eattrs; | |
480 | a0->eattrs = e; | |
481 | e->flags = EALF_SORTED; | |
482 | e->count = 1; | |
483 | e->attrs[0].id = EA_CODE(EAP_BGP, BA_NEXT_HOP); | |
484 | e->attrs[0].flags = BAF_TRANSITIVE; | |
485 | e->attrs[0].type = EAF_TYPE_IP_ADDRESS; | |
486 | e->attrs[0].u.ptr = ad; | |
487 | ad->length = 16; | |
488 | memcpy(ad->data, x+1, 16); | |
489 | len -= *x + 1; | |
490 | x += *x + 1; | |
491 | ||
492 | /* Ignore SNPA info */ | |
493 | i = *x++; | |
494 | while (i--) | |
495 | { | |
496 | if (len < 1 || len < 1 + *x) | |
497 | goto bad; | |
498 | len -= *x + 1; | |
499 | x += *x + 1; | |
500 | } | |
501 | ||
502 | if (bgp_get_nexthop(p, a0)) | |
503 | { | |
504 | a = rta_lookup(a0); | |
505 | while (len) | |
506 | { | |
507 | rte *e; | |
508 | DECODE_PREFIX(x, len); | |
509 | DBG("Add %I/%d\n", prefix, pxlen); | |
510 | e = rte_get_temp(rta_clone(a)); | |
511 | n = net_get(p->p.table, prefix, pxlen); | |
512 | e->net = n; | |
513 | e->pflags = 0; | |
514 | rte_update(p->p.table, n, &p->p, e); | |
515 | } | |
516 | rta_free(a); | |
517 | } | |
518 | } | |
519 | ||
520 | return; | |
521 | ||
522 | bad: | |
523 | bgp_error(conn, 3, 9, start, len0); | |
f8809249 MM |
524 | if (a) |
525 | rta_free(a); | |
1c1da87b MM |
526 | return; |
527 | } | |
528 | ||
529 | #endif | |
530 | ||
531 | static void | |
532 | bgp_rx_update(struct bgp_conn *conn, byte *pkt, int len) | |
533 | { | |
534 | struct bgp_proto *p = conn->bgp; | |
535 | byte *withdrawn, *attrs, *nlri; | |
536 | int withdrawn_len, attr_len, nlri_len; | |
537 | ||
538 | BGP_TRACE(D_PACKETS, "Got UPDATE"); | |
539 | if (conn->state != BS_ESTABLISHED) | |
540 | { bgp_error(conn, 5, 0, NULL, 0); return; } | |
541 | bgp_start_timer(conn->hold_timer, conn->hold_time); | |
542 | ||
543 | /* Find parts of the packet and check sizes */ | |
544 | if (len < 23) | |
545 | { | |
546 | bgp_error(conn, 1, 2, pkt+16, 2); | |
547 | return; | |
548 | } | |
549 | withdrawn = pkt + 21; | |
550 | withdrawn_len = get_u16(pkt + 19); | |
551 | if (withdrawn_len + 23 > len) | |
552 | goto malformed; | |
553 | attrs = withdrawn + withdrawn_len + 2; | |
554 | attr_len = get_u16(attrs - 2); | |
555 | if (withdrawn_len + attr_len + 23 > len) | |
556 | goto malformed; | |
557 | nlri = attrs + attr_len; | |
558 | nlri_len = len - withdrawn_len - attr_len - 23; | |
559 | if (!attr_len && nlri_len) | |
560 | goto malformed; | |
561 | DBG("Sizes: withdrawn=%d, attrs=%d, NLRI=%d\n", withdrawn_len, attr_len, nlri_len); | |
562 | ||
563 | lp_flush(bgp_linpool); | |
564 | ||
565 | bgp_do_rx_update(conn, withdrawn, withdrawn_len, nlri, nlri_len, attrs, attr_len); | |
566 | return; | |
567 | ||
568 | malformed: | |
efcece2d MM |
569 | bgp_error(conn, 3, 1, NULL, 0); |
570 | } | |
571 | ||
572 | static struct { | |
573 | byte major, minor; | |
574 | byte *msg; | |
575 | } bgp_msg_table[] = { | |
576 | { 1, 0, "Invalid message header" }, | |
577 | { 1, 1, "Connection not synchronized" }, | |
578 | { 1, 2, "Bad message length" }, | |
579 | { 1, 3, "Bad message type" }, | |
580 | { 2, 0, "Invalid OPEN message" }, | |
581 | { 2, 1, "Unsupported version number" }, | |
582 | { 2, 2, "Bad peer AS" }, | |
583 | { 2, 3, "Bad BGP identifier" }, | |
584 | { 2, 4, "Unsupported optional parameter" }, | |
585 | { 2, 5, "Authentication failure" }, | |
586 | { 2, 6, "Unacceptable hold time" }, | |
587 | { 2, 7, "Required capability missing" }, /* capability negotiation draft */ | |
588 | { 3, 0, "Invalid UPDATE message" }, | |
589 | { 3, 1, "Malformed attribute list" }, | |
590 | { 3, 2, "Unrecognized well-known attribute" }, | |
591 | { 3, 3, "Missing mandatory attribute" }, | |
592 | { 3, 4, "Invalid attribute flags" }, | |
593 | { 3, 5, "Invalid attribute length" }, | |
594 | { 3, 6, "Invalid ORIGIN attribute" }, | |
595 | { 3, 7, "AS routing loop" }, /* Deprecated */ | |
596 | { 3, 8, "Invalid NEXT_HOP attribute" }, | |
597 | { 3, 9, "Optional attribute error" }, | |
598 | { 3, 10, "Invalid network field" }, | |
599 | { 3, 11, "Malformed AS_PATH" }, | |
600 | { 4, 0, "Hold timer expired" }, | |
601 | { 5, 0, "Finite state machine error" }, | |
602 | { 6, 0, "Cease" } | |
603 | }; | |
604 | ||
605 | void | |
606 | bgp_log_error(struct bgp_proto *p, char *msg, unsigned code, unsigned subcode, byte *data, unsigned len) | |
607 | { | |
608 | byte *name, namebuf[16]; | |
609 | byte *t, argbuf[36]; | |
610 | unsigned i; | |
611 | ||
85733143 MM |
612 | if (code == 6 && !subcode) /* Don't report Cease messages */ |
613 | return; | |
614 | ||
efcece2d MM |
615 | bsprintf(namebuf, "%d.%d", code, subcode); |
616 | name = namebuf; | |
617 | for (i=0; i < ARRAY_SIZE(bgp_msg_table); i++) | |
618 | if (bgp_msg_table[i].major == code && bgp_msg_table[i].minor == subcode) | |
619 | { | |
620 | name = bgp_msg_table[i].msg; | |
621 | break; | |
622 | } | |
623 | t = argbuf; | |
624 | if (len) | |
625 | { | |
626 | *t++ = ':'; | |
627 | *t++ = ' '; | |
628 | if (len > 16) | |
629 | len = 16; | |
630 | for (i=0; i<len; i++) | |
631 | t += bsprintf(t, "%02x", data[i]); | |
632 | } | |
633 | *t = 0; | |
634 | log(L_REMOTE "%s: %s: %s%s", p->p.name, msg, name, argbuf); | |
3fdbafb6 MM |
635 | } |
636 | ||
637 | static void | |
638 | bgp_rx_notification(struct bgp_conn *conn, byte *pkt, int len) | |
639 | { | |
640 | unsigned arg; | |
641 | ||
642 | if (len < 21) | |
643 | { | |
efcece2d | 644 | bgp_error(conn, 1, 2, pkt+16, 2); |
3fdbafb6 MM |
645 | return; |
646 | } | |
efcece2d | 647 | bgp_log_error(conn->bgp, "Received error notification", pkt[19], pkt[20], pkt+21, len-21); |
3fdbafb6 | 648 | conn->error_flag = 1; |
b552ecc4 MM |
649 | if (conn->primary) |
650 | proto_notify_state(&conn->bgp->p, PS_STOP); | |
3fdbafb6 MM |
651 | bgp_schedule_packet(conn, PKT_SCHEDULE_CLOSE); |
652 | } | |
653 | ||
654 | static void | |
655 | bgp_rx_keepalive(struct bgp_conn *conn, byte *pkt, unsigned len) | |
656 | { | |
85368cd4 MM |
657 | struct bgp_proto *p = conn->bgp; |
658 | ||
659 | BGP_TRACE(D_PACKETS, "Got KEEPALIVE"); | |
3fdbafb6 MM |
660 | bgp_start_timer(conn->hold_timer, conn->hold_time); |
661 | switch (conn->state) | |
662 | { | |
663 | case BS_OPENCONFIRM: | |
664 | DBG("BGP: UP!!!\n"); | |
665 | conn->state = BS_ESTABLISHED; | |
ae8f5584 | 666 | bgp_attr_init(conn->bgp); |
3fdbafb6 MM |
667 | proto_notify_state(&conn->bgp->p, PS_UP); |
668 | break; | |
669 | case BS_ESTABLISHED: | |
670 | break; | |
671 | default: | |
efcece2d | 672 | bgp_error(conn, 5, 0, NULL, 0); |
3fdbafb6 MM |
673 | } |
674 | } | |
675 | ||
676 | static void | |
677 | bgp_rx_packet(struct bgp_conn *conn, byte *pkt, unsigned len) | |
678 | { | |
679 | DBG("BGP: Got packet %02x (%d bytes)\n", pkt[18], len); | |
680 | switch (pkt[18]) | |
681 | { | |
682 | case PKT_OPEN: return bgp_rx_open(conn, pkt, len); | |
683 | case PKT_UPDATE: return bgp_rx_update(conn, pkt, len); | |
684 | case PKT_NOTIFICATION: return bgp_rx_notification(conn, pkt, len); | |
685 | case PKT_KEEPALIVE: return bgp_rx_keepalive(conn, pkt, len); | |
efcece2d | 686 | default: bgp_error(conn, 1, 3, pkt+18, 1); |
3fdbafb6 | 687 | } |
72a6ef11 MM |
688 | } |
689 | ||
690 | int | |
691 | bgp_rx(sock *sk, int size) | |
692 | { | |
693 | struct bgp_conn *conn = sk->data; | |
694 | byte *pkt_start = sk->rbuf; | |
695 | byte *end = pkt_start + size; | |
3fdbafb6 | 696 | unsigned i, len; |
72a6ef11 MM |
697 | |
698 | DBG("BGP: RX hook: Got %d bytes\n", size); | |
699 | while (end >= pkt_start + BGP_HEADER_LENGTH) | |
700 | { | |
701 | if (conn->error_flag) | |
3fdbafb6 | 702 | { |
efcece2d MM |
703 | /* |
704 | * We still need to remember the erroneous packet, so that | |
705 | * we can generate error notifications properly. To avoid | |
706 | * subsequent reads rewriting the buffer, we just reset the | |
707 | * rx_hook. | |
708 | */ | |
3fdbafb6 | 709 | DBG("BGP: Error, dropping input\n"); |
efcece2d MM |
710 | sk->rx_hook = NULL; |
711 | return 0; | |
3fdbafb6 MM |
712 | } |
713 | for(i=0; i<16; i++) | |
714 | if (pkt_start[i] != 0xff) | |
715 | { | |
efcece2d | 716 | bgp_error(conn, 1, 1, NULL, 0); |
3fdbafb6 MM |
717 | break; |
718 | } | |
719 | len = get_u16(pkt_start+16); | |
720 | if (len < BGP_HEADER_LENGTH || len > BGP_MAX_PACKET_LENGTH) | |
721 | { | |
efcece2d | 722 | bgp_error(conn, 1, 2, pkt_start+16, 2); |
3fdbafb6 MM |
723 | break; |
724 | } | |
5f532add MM |
725 | if (end < pkt_start + len) |
726 | break; | |
727 | bgp_rx_packet(conn, pkt_start, len); | |
728 | pkt_start += len; | |
72a6ef11 MM |
729 | } |
730 | if (pkt_start != sk->rbuf) | |
731 | { | |
732 | memmove(sk->rbuf, pkt_start, end - pkt_start); | |
733 | sk->rpos = sk->rbuf + (end - pkt_start); | |
734 | } | |
735 | return 0; | |
736 | } |