]>
Commit | Line | Data |
---|---|---|
8465dccb OZ |
1 | /* |
2 | * BIRD -- Routing Information Protocol (RIP) | |
3 | * | |
4 | * (c) 1998--1999 Pavel Machek <pavel@ucw.cz> | |
5 | * (c) 2004--2013 Ondrej Filip <feela@network.cz> | |
6 | * (c) 2009--2015 Ondrej Zajicek <santiago@crfreenet.org> | |
7 | * (c) 2009--2015 CZ.NIC z.s.p.o. | |
8 | * | |
9 | * Can be freely distributed and used under the terms of the GNU GPL. | |
10 | */ | |
11 | ||
12 | #include "rip.h" | |
de2a27e2 | 13 | #include "lib/mac.h" |
8465dccb OZ |
14 | |
15 | ||
16 | #define RIP_CMD_REQUEST 1 /* want info */ | |
17 | #define RIP_CMD_RESPONSE 2 /* responding to request */ | |
18 | ||
19 | #define RIP_BLOCK_LENGTH 20 | |
8465dccb | 20 | #define RIP_PASSWD_LENGTH 16 |
8465dccb OZ |
21 | |
22 | #define RIP_AF_IPV4 2 | |
23 | #define RIP_AF_AUTH 0xffff | |
24 | ||
25 | ||
26 | /* RIP packet header */ | |
27 | struct rip_packet | |
28 | { | |
29 | u8 command; | |
30 | u8 version; | |
31 | u16 unused; | |
32 | }; | |
33 | ||
34 | /* RTE block for RIPv2 */ | |
35 | struct rip_block_v2 | |
36 | { | |
37 | u16 family; | |
38 | u16 tag; | |
39 | ip4_addr network; | |
40 | ip4_addr netmask; | |
41 | ip4_addr next_hop; | |
42 | u32 metric; | |
43 | }; | |
44 | ||
45 | /* RTE block for RIPng */ | |
46 | struct rip_block_ng | |
47 | { | |
48 | ip6_addr prefix; | |
49 | u16 tag; | |
50 | u8 pxlen; | |
51 | u8 metric; | |
52 | }; | |
53 | ||
54 | /* Authentication block for RIPv2 */ | |
55 | struct rip_block_auth | |
56 | { | |
57 | u16 must_be_ffff; | |
58 | u16 auth_type; | |
59 | char password[0]; | |
60 | u16 packet_len; | |
61 | u8 key_id; | |
62 | u8 auth_len; | |
63 | u32 seq_num; | |
64 | u32 unused1; | |
65 | u32 unused2; | |
66 | }; | |
67 | ||
68 | /* Authentication tail, RFC 4822 */ | |
69 | struct rip_auth_tail | |
70 | { | |
71 | u16 must_be_ffff; | |
72 | u16 must_be_0001; | |
390601f0 | 73 | byte auth_data[0]; |
8465dccb OZ |
74 | }; |
75 | ||
76 | /* Internal representation of RTE block data */ | |
77 | struct rip_block | |
78 | { | |
79 | ip_addr prefix; | |
80 | int pxlen; | |
81 | u32 metric; | |
82 | u16 tag; | |
83 | u16 no_af; | |
84 | ip_addr next_hop; | |
85 | }; | |
86 | ||
87 | ||
88 | #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) | |
89 | #define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0) | |
90 | #define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0) | |
91 | ||
92 | #define LOG_PKT(msg, args...) \ | |
93 | log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args) | |
94 | ||
95 | #define LOG_PKT_AUTH(msg, args...) \ | |
96 | log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args) | |
97 | ||
98 | #define LOG_RTE(msg, args...) \ | |
99 | log_rl(&p->log_rte_tbf, L_REMOTE "%s: " msg, p->p.name, args) | |
100 | ||
101 | ||
102 | static inline void * rip_tx_buffer(struct rip_iface *ifa) | |
103 | { return ifa->sk->tbuf; } | |
104 | ||
105 | static inline uint rip_pkt_hdrlen(struct rip_iface *ifa) | |
106 | { return sizeof(struct rip_packet) + (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); } | |
107 | ||
108 | static inline void | |
3e236955 | 109 | rip_put_block(struct rip_proto *p UNUSED4 UNUSED6, byte *pos, struct rip_block *rte) |
8465dccb OZ |
110 | { |
111 | if (rip_is_v2(p)) | |
112 | { | |
113 | struct rip_block_v2 *block = (void *) pos; | |
114 | block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4); | |
115 | block->tag = htons(rte->tag); | |
116 | block->network = ip4_hton(ipa_to_ip4(rte->prefix)); | |
117 | block->netmask = ip4_hton(ip4_mkmask(rte->pxlen)); | |
118 | block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop)); | |
119 | block->metric = htonl(rte->metric); | |
120 | } | |
121 | else /* RIPng */ | |
122 | { | |
123 | struct rip_block_ng *block = (void *) pos; | |
124 | block->prefix = ip6_hton(ipa_to_ip6(rte->prefix)); | |
125 | block->tag = htons(rte->tag); | |
126 | block->pxlen = rte->pxlen; | |
127 | block->metric = rte->metric; | |
128 | } | |
129 | } | |
130 | ||
131 | static inline void | |
3e236955 | 132 | rip_put_next_hop(struct rip_proto *p UNUSED, byte *pos, struct rip_block *rte UNUSED4) |
8465dccb OZ |
133 | { |
134 | struct rip_block_ng *block = (void *) pos; | |
135 | block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop)); | |
136 | block->tag = 0; | |
137 | block->pxlen = 0; | |
138 | block->metric = 0xff; | |
139 | } | |
140 | ||
141 | static inline int | |
3e236955 | 142 | rip_get_block(struct rip_proto *p UNUSED4 UNUSED6, byte *pos, struct rip_block *rte) |
8465dccb OZ |
143 | { |
144 | if (rip_is_v2(p)) | |
145 | { | |
146 | struct rip_block_v2 *block = (void *) pos; | |
147 | ||
148 | /* Skip blocks with strange AF, including authentication blocks */ | |
149 | if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4))) | |
150 | return 0; | |
151 | ||
152 | rte->prefix = ipa_from_ip4(ip4_ntoh(block->network)); | |
153 | rte->pxlen = ip4_masklen(ip4_ntoh(block->netmask)); | |
154 | rte->metric = ntohl(block->metric); | |
155 | rte->tag = ntohs(block->tag); | |
156 | rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop)); | |
157 | ||
158 | return 1; | |
159 | } | |
160 | else /* RIPng */ | |
161 | { | |
162 | struct rip_block_ng *block = (void *) pos; | |
163 | ||
164 | /* Handle and skip next hop blocks */ | |
165 | if (block->metric == 0xff) | |
166 | { | |
167 | rte->next_hop = ipa_from_ip6(ip6_ntoh(block->prefix)); | |
168 | if (!ipa_is_link_local(rte->next_hop)) rte->next_hop = IPA_NONE; | |
169 | return 0; | |
170 | } | |
171 | ||
172 | rte->prefix = ipa_from_ip6(ip6_ntoh(block->prefix)); | |
173 | rte->pxlen = block->pxlen; | |
174 | rte->metric = block->metric; | |
175 | rte->tag = ntohs(block->tag); | |
176 | /* rte->next_hop is deliberately kept unmodified */; | |
177 | ||
178 | return 1; | |
179 | } | |
180 | } | |
181 | ||
182 | static inline void | |
183 | rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa) | |
184 | { | |
185 | /* | |
186 | * We update crypto sequence numbers at the beginning of update session to | |
187 | * avoid issues with packet reordering, so packets inside one update session | |
188 | * have the same CSN. We are using real time, but enforcing monotonicity. | |
189 | */ | |
190 | if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) | |
191 | ifa->csn = (ifa->csn < (u32) now_real) ? (u32) now_real : ifa->csn + 1; | |
192 | } | |
193 | ||
194 | static void | |
195 | rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen) | |
196 | { | |
197 | struct rip_block_auth *auth = (void *) (pkt + 1); | |
198 | struct password_item *pass = password_find(ifa->cf->passwords, 0); | |
199 | ||
200 | if (!pass) | |
201 | { | |
202 | /* FIXME: This should not happen */ | |
203 | log(L_ERR "%s: No suitable password found for authentication", p->p.name); | |
204 | memset(auth, 0, sizeof(struct rip_block_auth)); | |
205 | return; | |
206 | } | |
207 | ||
208 | switch (ifa->cf->auth_type) | |
209 | { | |
210 | case RIP_AUTH_PLAIN: | |
211 | auth->must_be_ffff = htons(0xffff); | |
212 | auth->auth_type = htons(RIP_AUTH_PLAIN); | |
213 | strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH); | |
214 | return; | |
215 | ||
216 | case RIP_AUTH_CRYPTO: | |
217 | auth->must_be_ffff = htons(0xffff); | |
218 | auth->auth_type = htons(RIP_AUTH_CRYPTO); | |
219 | auth->packet_len = htons(*plen); | |
220 | auth->key_id = pass->id; | |
390601f0 | 221 | auth->auth_len = mac_type_length(pass->alg); |
8465dccb OZ |
222 | auth->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0; |
223 | auth->unused1 = 0; | |
224 | auth->unused2 = 0; | |
225 | ifa->csn_ready = 1; | |
226 | ||
390601f0 OZ |
227 | if (pass->alg < ALG_HMAC) |
228 | auth->auth_len += sizeof(struct rip_auth_tail); | |
229 | ||
8465dccb OZ |
230 | /* |
231 | * Note that RFC 4822 is unclear whether auth_len should cover whole | |
232 | * authentication trailer or just auth_data length. | |
233 | * | |
390601f0 OZ |
234 | * FIXME: We should use just auth_data length by default. Currently we put |
235 | * the whole auth trailer length in keyed hash case to keep old behavior, | |
236 | * but we put just auth_data length in the new HMAC case. Note that Quagga | |
237 | * has config option for this. | |
238 | * | |
8465dccb OZ |
239 | * Crypto sequence numbers are increased by sender in rip_update_csn(). |
240 | * First CSN should be zero, this is handled by csn_ready. | |
241 | */ | |
242 | ||
243 | struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen); | |
244 | tail->must_be_ffff = htons(0xffff); | |
245 | tail->must_be_0001 = htons(0x0001); | |
8465dccb | 246 | |
390601f0 OZ |
247 | uint auth_len = mac_type_length(pass->alg); |
248 | *plen += sizeof(struct rip_auth_tail) + auth_len; | |
8465dccb | 249 | |
390601f0 OZ |
250 | /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ |
251 | if (pass->alg < ALG_HMAC) | |
252 | strncpy(tail->auth_data, pass->password, auth_len); | |
253 | else | |
254 | memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); | |
255 | ||
256 | mac_fill(pass->alg, pass->password, pass->length, | |
257 | (byte *) pkt, *plen, tail->auth_data); | |
8465dccb OZ |
258 | return; |
259 | ||
260 | default: | |
261 | bug("Unknown authentication type"); | |
262 | } | |
263 | } | |
264 | ||
265 | static int | |
266 | rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, struct rip_neighbor *n) | |
267 | { | |
268 | struct rip_block_auth *auth = (void *) (pkt + 1); | |
269 | struct password_item *pass = NULL; | |
270 | const char *err_dsc = NULL; | |
271 | uint err_val = 0; | |
272 | uint auth_type = 0; | |
273 | ||
274 | /* Check for authentication entry */ | |
275 | if ((*plen >= (sizeof(struct rip_packet) + sizeof(struct rip_block_auth))) && | |
276 | (auth->must_be_ffff == htons(0xffff))) | |
277 | auth_type = ntohs(auth->auth_type); | |
278 | ||
279 | if (auth_type != ifa->cf->auth_type) | |
280 | DROP("authentication method mismatch", auth_type); | |
281 | ||
282 | switch (auth_type) | |
283 | { | |
284 | case RIP_AUTH_NONE: | |
285 | return 1; | |
286 | ||
287 | case RIP_AUTH_PLAIN: | |
288 | pass = password_find_by_value(ifa->cf->passwords, auth->password, RIP_PASSWD_LENGTH); | |
289 | if (!pass) | |
290 | DROP1("wrong password"); | |
291 | ||
292 | return 1; | |
293 | ||
294 | case RIP_AUTH_CRYPTO: | |
295 | pass = password_find_by_id(ifa->cf->passwords, auth->key_id); | |
296 | if (!pass) | |
297 | DROP("no suitable password found", auth->key_id); | |
298 | ||
299 | uint data_len = ntohs(auth->packet_len); | |
390601f0 OZ |
300 | uint auth_len = mac_type_length(pass->alg); |
301 | uint auth_len2 = sizeof(struct rip_auth_tail) + auth_len; | |
302 | ||
303 | /* | |
304 | * Ideally, first check should be check for internal consistency: | |
305 | * (data_len + sizeof(struct rip_auth_tail) + auth->auth_len) != *plen | |
306 | * | |
307 | * Second one should check expected code length: | |
308 | * auth->auth_len != auth_len | |
309 | * | |
310 | * But as auth->auth_len has two interpretations, we simplify this | |
311 | */ | |
8465dccb | 312 | |
390601f0 OZ |
313 | if (data_len + auth_len2 != *plen) |
314 | DROP("packet length mismatch", *plen); | |
8465dccb | 315 | |
390601f0 OZ |
316 | /* Warning: two interpretations of auth_len field */ |
317 | if ((auth->auth_len != auth_len) && (auth->auth_len != auth_len2)) | |
318 | DROP("wrong authentication length", auth->auth_len); | |
8465dccb OZ |
319 | |
320 | struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len); | |
321 | if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001))) | |
322 | DROP1("authentication trailer is missing"); | |
323 | ||
324 | /* Accept higher sequence number, or zero if connectivity is lost */ | |
325 | /* FIXME: sequence number must be password/SA specific */ | |
326 | u32 rcv_csn = ntohl(auth->seq_num); | |
327 | if ((rcv_csn < n->csn) && (rcv_csn || n->uc)) | |
328 | { | |
329 | /* We want to report both new and old CSN */ | |
330 | LOG_PKT_AUTH("Authentication failed for %I on %s - " | |
331 | "lower sequence number (rcv %u, old %u)", | |
332 | n->nbr->addr, ifa->iface->name, rcv_csn, n->csn); | |
333 | return 0; | |
334 | } | |
335 | ||
390601f0 OZ |
336 | byte *auth_data = alloca(auth_len); |
337 | memcpy(auth_data, tail->auth_data, auth_len); | |
8465dccb | 338 | |
390601f0 OZ |
339 | /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ |
340 | if (pass->alg < ALG_HMAC) | |
341 | strncpy(tail->auth_data, pass->password, auth_len); | |
342 | else | |
343 | memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); | |
8465dccb | 344 | |
390601f0 OZ |
345 | if (!mac_verify(pass->alg, pass->password, pass->length, |
346 | (byte *) pkt, *plen, auth_data)) | |
347 | DROP("wrong authentication code", pass->id); | |
8465dccb OZ |
348 | |
349 | *plen = data_len; | |
350 | n->csn = rcv_csn; | |
351 | ||
352 | return 1; | |
353 | } | |
354 | ||
355 | drop: | |
356 | LOG_PKT_AUTH("Authentication failed for %I on %s - %s (%u)", | |
357 | n->nbr->addr, ifa->iface->name, err_dsc, err_val); | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
362 | static inline int | |
363 | rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, ip_addr dst) | |
364 | { | |
365 | if (ifa->cf->auth_type) | |
366 | rip_fill_authentication(p, ifa, pkt, &plen); | |
367 | ||
368 | return sk_send_to(ifa->sk, plen, dst, 0); | |
369 | } | |
370 | ||
371 | ||
372 | void | |
373 | rip_send_request(struct rip_proto *p, struct rip_iface *ifa) | |
374 | { | |
375 | byte *pos = rip_tx_buffer(ifa); | |
376 | ||
377 | struct rip_packet *pkt = (void *) pos; | |
378 | pkt->command = RIP_CMD_REQUEST; | |
379 | pkt->version = ifa->cf->version; | |
380 | pkt->unused = 0; | |
381 | pos += rip_pkt_hdrlen(ifa); | |
382 | ||
383 | struct rip_block b = { .no_af = 1, .metric = p->infinity }; | |
384 | rip_put_block(p, pos, &b); | |
385 | pos += RIP_BLOCK_LENGTH; | |
386 | ||
387 | rip_update_csn(p, ifa); | |
388 | ||
389 | TRACE(D_PACKETS, "Sending request via %s", ifa->iface->name); | |
390 | rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->addr); | |
391 | } | |
392 | ||
393 | static void | |
394 | rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) | |
395 | { | |
396 | TRACE(D_PACKETS, "Request received from %I on %s", from->nbr->addr, ifa->iface->name); | |
397 | ||
398 | byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); | |
399 | ||
400 | /* We expect one regular block */ | |
401 | if (plen != (rip_pkt_hdrlen(ifa) + RIP_BLOCK_LENGTH)) | |
402 | return; | |
403 | ||
404 | struct rip_block b = { .no_af = 1 }; | |
405 | ||
406 | if (!rip_get_block(p, pos, &b)) | |
407 | return; | |
408 | ||
409 | /* Special case - zero prefix, infinity metric */ | |
410 | if (ipa_nonzero(b.prefix) || b.pxlen || (b.metric != p->infinity)) | |
411 | return; | |
412 | ||
413 | /* We do nothing if TX is already active */ | |
414 | if (ifa->tx_active) | |
415 | { | |
416 | TRACE(D_EVENTS, "Skipping request from %I on %s, TX is busy", from->nbr->addr, ifa->iface->name); | |
417 | return; | |
418 | } | |
419 | ||
420 | if (!ifa->cf->passive) | |
421 | rip_send_table(p, ifa, from->nbr->addr, 0); | |
422 | } | |
423 | ||
424 | ||
425 | static int | |
426 | rip_send_response(struct rip_proto *p, struct rip_iface *ifa) | |
427 | { | |
428 | if (! ifa->tx_active) | |
429 | return 0; | |
430 | ||
431 | byte *pos = rip_tx_buffer(ifa); | |
432 | byte *max = rip_tx_buffer(ifa) + ifa->tx_plen - | |
433 | (rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH); | |
434 | ip_addr last_next_hop = IPA_NONE; | |
435 | int send = 0; | |
436 | ||
437 | struct rip_packet *pkt = (void *) pos; | |
438 | pkt->command = RIP_CMD_RESPONSE; | |
439 | pkt->version = ifa->cf->version; | |
440 | pkt->unused = 0; | |
441 | pos += rip_pkt_hdrlen(ifa); | |
442 | ||
443 | FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, z) | |
444 | { | |
445 | struct rip_entry *en = (struct rip_entry *) z; | |
446 | ||
447 | /* Dummy entries */ | |
448 | if (!en->valid) | |
449 | goto next_entry; | |
450 | ||
451 | /* Stale entries that should be removed */ | |
452 | if ((en->valid == RIP_ENTRY_STALE) && | |
453 | ((en->changed + ifa->cf->garbage_time) <= now)) | |
454 | goto next_entry; | |
455 | ||
456 | /* Triggered updates */ | |
457 | if (en->changed < ifa->tx_changed) | |
458 | goto next_entry; | |
459 | ||
460 | /* Not enough space for current entry */ | |
461 | if (pos > max) | |
462 | { | |
463 | FIB_ITERATE_PUT(&ifa->tx_fit, z); | |
464 | goto break_loop; | |
465 | } | |
466 | ||
467 | struct rip_block rte = { | |
468 | .prefix = en->n.prefix, | |
469 | .pxlen = en->n.pxlen, | |
470 | .metric = en->metric, | |
471 | .tag = en->tag | |
472 | }; | |
473 | ||
474 | if (en->iface == ifa->iface) | |
475 | rte.next_hop = en->next_hop; | |
476 | ||
477 | if (rip_is_v2(p) && (ifa->cf->version == RIP_V1)) | |
478 | { | |
479 | /* Skipping subnets (i.e. not hosts, classful networks or default route) */ | |
480 | if (ip4_masklen(ip4_class_mask(ipa_to_ip4(en->n.prefix))) != en->n.pxlen) | |
481 | goto next_entry; | |
482 | ||
483 | rte.tag = 0; | |
484 | rte.pxlen = 0; | |
485 | rte.next_hop = IPA_NONE; | |
486 | } | |
487 | ||
488 | /* Split horizon */ | |
489 | if (en->from == ifa->iface && ifa->cf->split_horizon) | |
490 | { | |
491 | if (ifa->cf->poison_reverse) | |
492 | { | |
493 | rte.metric = p->infinity; | |
494 | rte.next_hop = IPA_NONE; | |
495 | } | |
496 | else | |
497 | goto next_entry; | |
498 | } | |
499 | ||
500 | // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); | |
501 | ||
502 | /* RIPng next hop entry */ | |
503 | if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop)) | |
504 | { | |
505 | last_next_hop = rte.next_hop; | |
506 | rip_put_next_hop(p, pos, &rte); | |
507 | pos += RIP_BLOCK_LENGTH; | |
508 | } | |
509 | ||
510 | rip_put_block(p, pos, &rte); | |
511 | pos += RIP_BLOCK_LENGTH; | |
512 | send = 1; | |
513 | ||
514 | next_entry: ; | |
515 | } | |
516 | FIB_ITERATE_END(z); | |
517 | ifa->tx_active = 0; | |
518 | ||
519 | /* Do not send empty packet */ | |
520 | if (!send) | |
521 | return 0; | |
522 | ||
523 | break_loop: | |
524 | TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name); | |
525 | return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr); | |
526 | } | |
527 | ||
528 | /** | |
529 | * rip_send_table - RIP interface timer hook | |
530 | * @p: RIP instance | |
531 | * @ifa: RIP interface | |
532 | * @addr: destination IP address | |
533 | * @changed: time limit for triggered updates | |
534 | * | |
535 | * The function activates an update session and starts sending routing update | |
536 | * packets (using rip_send_response()). The session may be finished during the | |
537 | * call or may continue in rip_tx_hook() until all appropriate routes are | |
538 | * transmitted. Note that there may be at most one active update session per | |
539 | * interface, the function will terminate the old active session before | |
540 | * activating the new one. | |
541 | */ | |
542 | void | |
543 | rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, bird_clock_t changed) | |
544 | { | |
545 | DBG("RIP: Opening TX session to %I on %s\n", dst, ifa->iface->name); | |
546 | ||
547 | rip_reset_tx_session(p, ifa); | |
548 | ||
549 | ifa->tx_active = 1; | |
550 | ifa->tx_addr = addr; | |
551 | ifa->tx_changed = changed; | |
552 | FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable); | |
553 | ||
554 | rip_update_csn(p, ifa); | |
555 | ||
556 | while (rip_send_response(p, ifa) > 0) | |
557 | ; | |
558 | } | |
559 | ||
560 | static void | |
561 | rip_tx_hook(sock *sk) | |
562 | { | |
563 | struct rip_iface *ifa = sk->data; | |
564 | struct rip_proto *p = ifa->rip; | |
565 | ||
566 | DBG("RIP: TX hook called (iface %s, src %I, dst %I)\n", | |
567 | sk->iface->name, sk->saddr, sk->daddr); | |
568 | ||
569 | while (rip_send_response(p, ifa) > 0) | |
570 | ; | |
571 | } | |
572 | ||
573 | static void | |
574 | rip_err_hook(sock *sk, int err) | |
575 | { | |
576 | struct rip_iface *ifa = sk->data; | |
577 | struct rip_proto *p = ifa->rip; | |
578 | ||
579 | log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->iface->name, err); | |
580 | ||
581 | rip_reset_tx_session(p, ifa); | |
582 | } | |
583 | ||
584 | static void | |
585 | rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) | |
586 | { | |
587 | struct rip_block rte = {}; | |
588 | const char *err_dsc = NULL; | |
589 | ||
590 | TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name); | |
591 | ||
592 | byte *pos = (byte *) pkt + sizeof(struct rip_packet); | |
593 | byte *end = (byte *) pkt + plen; | |
594 | ||
595 | for (; pos < end; pos += RIP_BLOCK_LENGTH) | |
596 | { | |
597 | /* Find next regular RTE */ | |
598 | if (!rip_get_block(p, pos, &rte)) | |
599 | continue; | |
600 | ||
601 | int c = ipa_classify_net(rte.prefix); | |
602 | if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) | |
603 | SKIP("invalid prefix"); | |
604 | ||
605 | if (rip_is_v2(p) && (pkt->version == RIP_V1)) | |
606 | { | |
607 | if (ifa->cf->check_zero && (rte.tag || rte.pxlen || ipa_nonzero(rte.next_hop))) | |
608 | SKIP("RIPv1 reserved field is nonzero"); | |
609 | ||
610 | rte.tag = 0; | |
611 | rte.pxlen = ip4_masklen(ip4_class_mask(ipa_to_ip4(rte.prefix))); | |
612 | rte.next_hop = IPA_NONE; | |
613 | } | |
614 | ||
615 | if ((rte.pxlen < 0) || (rte.pxlen > MAX_PREFIX_LENGTH)) | |
616 | SKIP("invalid prefix length"); | |
617 | ||
618 | if (rte.metric > p->infinity) | |
619 | SKIP("invalid metric"); | |
620 | ||
621 | if (ipa_nonzero(rte.next_hop)) | |
622 | { | |
623 | neighbor *nbr = neigh_find2(&p->p, &rte.next_hop, ifa->iface, 0); | |
624 | if (!nbr || (nbr->scope <= 0)) | |
625 | rte.next_hop = IPA_NONE; | |
626 | } | |
627 | ||
628 | // TRACE(D_PACKETS, " %I/%d -> %I metric %d", rte.prefix, rte.pxlen, rte.next_hop, rte.metric); | |
629 | ||
630 | rte.metric += ifa->cf->metric; | |
631 | ||
632 | if (rte.metric < p->infinity) | |
633 | { | |
634 | struct rip_rte new = { | |
635 | .from = from, | |
636 | .next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr, | |
637 | .metric = rte.metric, | |
638 | .tag = rte.tag, | |
639 | .expires = now + ifa->cf->timeout_time | |
640 | }; | |
641 | ||
642 | rip_update_rte(p, &rte.prefix, rte.pxlen, &new); | |
643 | } | |
644 | else | |
645 | rip_withdraw_rte(p, &rte.prefix, rte.pxlen, from); | |
646 | ||
647 | continue; | |
648 | ||
649 | skip: | |
650 | LOG_RTE("Ignoring route %I/%d received from %I - %s", | |
651 | rte.prefix, rte.pxlen, from->nbr->addr, err_dsc); | |
652 | } | |
653 | } | |
654 | ||
655 | static int | |
3e236955 | 656 | rip_rx_hook(sock *sk, uint len) |
8465dccb OZ |
657 | { |
658 | struct rip_iface *ifa = sk->data; | |
659 | struct rip_proto *p = ifa->rip; | |
660 | const char *err_dsc = NULL; | |
661 | uint err_val = 0; | |
662 | ||
663 | if (sk->lifindex != sk->iface->index) | |
664 | return 1; | |
665 | ||
666 | DBG("RIP: RX hook called (iface %s, src %I, dst %I)\n", | |
667 | sk->iface->name, sk->faddr, sk->laddr); | |
668 | ||
669 | /* Silently ignore my own packets */ | |
670 | /* FIXME: Better local address check */ | |
671 | if (ipa_equal(ifa->iface->addr->ip, sk->faddr)) | |
672 | return 1; | |
673 | ||
674 | if (rip_is_ng(p) && !ipa_is_link_local(sk->faddr)) | |
675 | DROP1("wrong src address"); | |
676 | ||
677 | struct rip_neighbor *n = rip_get_neighbor(p, &sk->faddr, ifa); | |
678 | ||
679 | if (!n) | |
680 | DROP1("not from neighbor"); | |
681 | ||
682 | if ((ifa->cf->ttl_security == 1) && (sk->rcv_ttl < 255)) | |
683 | DROP("wrong TTL", sk->rcv_ttl); | |
684 | ||
685 | if (sk->fport != sk->dport) | |
686 | DROP("wrong src port", sk->fport); | |
687 | ||
688 | if (len < sizeof(struct rip_packet)) | |
689 | DROP("too short", len); | |
690 | ||
691 | if (sk->flags & SKF_TRUNCATED) | |
692 | DROP("truncated", len); | |
693 | ||
694 | struct rip_packet *pkt = (struct rip_packet *) sk->rbuf; | |
695 | uint plen = len; | |
696 | ||
697 | if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version))) | |
698 | DROP("wrong version", pkt->version); | |
699 | ||
700 | /* rip_check_authentication() has its own error logging */ | |
701 | if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &plen, n)) | |
702 | return 1; | |
703 | ||
704 | if ((plen - sizeof(struct rip_packet)) % RIP_BLOCK_LENGTH) | |
705 | DROP("invalid length", plen); | |
706 | ||
707 | n->last_seen = now; | |
708 | rip_update_bfd(p, n); | |
709 | ||
710 | switch (pkt->command) | |
711 | { | |
712 | case RIP_CMD_REQUEST: | |
713 | rip_receive_request(p, ifa, pkt, plen, n); | |
714 | break; | |
715 | ||
716 | case RIP_CMD_RESPONSE: | |
717 | rip_receive_response(p, ifa, pkt, plen, n); | |
718 | break; | |
719 | ||
720 | default: | |
721 | DROP("unknown command", pkt->command); | |
722 | } | |
723 | return 1; | |
724 | ||
725 | drop: | |
726 | LOG_PKT("Bad packet from %I via %s - %s (%u)", | |
727 | sk->faddr, sk->iface->name, err_dsc, err_val); | |
728 | ||
729 | return 1; | |
730 | } | |
731 | ||
732 | int | |
733 | rip_open_socket(struct rip_iface *ifa) | |
734 | { | |
735 | struct rip_proto *p = ifa->rip; | |
736 | ||
737 | sock *sk = sk_new(p->p.pool); | |
738 | sk->type = SK_UDP; | |
739 | sk->sport = ifa->cf->port; | |
740 | sk->dport = ifa->cf->port; | |
741 | sk->iface = ifa->iface; | |
943478b0 | 742 | sk->vrf = p->p.vrf; |
8465dccb OZ |
743 | |
744 | /* | |
745 | * For RIPv2, we explicitly choose a primary address, mainly to ensure that | |
746 | * RIP and BFD uses the same one. For RIPng, we left it to kernel, which | |
747 | * should choose some link-local address based on the same scope rule. | |
748 | */ | |
749 | if (rip_is_v2(p)) | |
750 | sk->saddr = ifa->iface->addr->ip; | |
751 | ||
752 | sk->rx_hook = rip_rx_hook; | |
753 | sk->tx_hook = rip_tx_hook; | |
754 | sk->err_hook = rip_err_hook; | |
755 | sk->data = ifa; | |
756 | ||
757 | sk->tos = ifa->cf->tx_tos; | |
758 | sk->priority = ifa->cf->tx_priority; | |
759 | sk->ttl = ifa->cf->ttl_security ? 255 : 1; | |
760 | sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0); | |
761 | ||
762 | /* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */ | |
763 | ||
764 | if (sk_open(sk) < 0) | |
765 | goto err; | |
766 | ||
767 | if (ifa->cf->mode == RIP_IM_MULTICAST) | |
768 | { | |
769 | if (sk_setup_multicast(sk) < 0) | |
770 | goto err; | |
771 | ||
772 | if (sk_join_group(sk, ifa->addr) < 0) | |
773 | goto err; | |
774 | } | |
775 | else /* Broadcast */ | |
776 | { | |
777 | if (sk_setup_broadcast(sk) < 0) | |
778 | goto err; | |
779 | ||
780 | if (ipa_zero(ifa->addr)) | |
781 | { | |
782 | sk->err = "Missing broadcast address"; | |
783 | goto err; | |
784 | } | |
785 | } | |
786 | ||
787 | ifa->sk = sk; | |
788 | return 1; | |
789 | ||
790 | err: | |
791 | sk_log_error(sk, p->p.name); | |
792 | rfree(sk); | |
793 | return 0; | |
794 | } |