]>
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 | ||
d82987d1 PT |
12 | #undef LOCAL_DEBUG |
13 | ||
8465dccb | 14 | #include "rip.h" |
de2a27e2 | 15 | #include "lib/mac.h" |
8465dccb OZ |
16 | |
17 | ||
18 | #define RIP_CMD_REQUEST 1 /* want info */ | |
19 | #define RIP_CMD_RESPONSE 2 /* responding to request */ | |
20 | ||
22c3cf95 OZ |
21 | #define RIP_CMD_UPDATE_REQUEST 9 |
22 | #define RIP_CMD_UPDATE_RESPONSE 10 | |
23 | #define RIP_CMD_UPDATE_ACK 11 | |
24 | ||
8465dccb | 25 | #define RIP_BLOCK_LENGTH 20 |
8465dccb | 26 | #define RIP_PASSWD_LENGTH 16 |
8465dccb OZ |
27 | |
28 | #define RIP_AF_IPV4 2 | |
29 | #define RIP_AF_AUTH 0xffff | |
30 | ||
22c3cf95 OZ |
31 | #define RIP_UPDATE_VERSION 1 |
32 | ||
8465dccb OZ |
33 | |
34 | /* RIP packet header */ | |
35 | struct rip_packet | |
36 | { | |
37 | u8 command; | |
38 | u8 version; | |
39 | u16 unused; | |
40 | }; | |
41 | ||
22c3cf95 OZ |
42 | /* Triggered RIP update header (RFC 2091) */ |
43 | struct rip_update_hdr | |
44 | { | |
45 | u8 version; | |
46 | u8 flush; | |
47 | u16 seqnum; | |
48 | }; | |
49 | ||
8465dccb OZ |
50 | /* RTE block for RIPv2 */ |
51 | struct rip_block_v2 | |
52 | { | |
53 | u16 family; | |
54 | u16 tag; | |
55 | ip4_addr network; | |
56 | ip4_addr netmask; | |
57 | ip4_addr next_hop; | |
58 | u32 metric; | |
59 | }; | |
60 | ||
61 | /* RTE block for RIPng */ | |
62 | struct rip_block_ng | |
63 | { | |
64 | ip6_addr prefix; | |
65 | u16 tag; | |
66 | u8 pxlen; | |
67 | u8 metric; | |
68 | }; | |
69 | ||
70 | /* Authentication block for RIPv2 */ | |
71 | struct rip_block_auth | |
72 | { | |
73 | u16 must_be_ffff; | |
74 | u16 auth_type; | |
d4cebc6b JMM |
75 | union { |
76 | char password[16]; | |
77 | struct { | |
78 | u16 packet_len; | |
79 | u8 key_id; | |
80 | u8 auth_len; | |
81 | u32 seq_num; | |
82 | u32 unused1; | |
83 | u32 unused2; | |
84 | }; | |
85 | }; | |
8465dccb OZ |
86 | }; |
87 | ||
88 | /* Authentication tail, RFC 4822 */ | |
89 | struct rip_auth_tail | |
90 | { | |
91 | u16 must_be_ffff; | |
92 | u16 must_be_0001; | |
390601f0 | 93 | byte auth_data[0]; |
8465dccb OZ |
94 | }; |
95 | ||
96 | /* Internal representation of RTE block data */ | |
97 | struct rip_block | |
98 | { | |
fe9f1a6d | 99 | net_addr net; |
8465dccb OZ |
100 | u32 metric; |
101 | u16 tag; | |
102 | u16 no_af; | |
103 | ip_addr next_hop; | |
104 | }; | |
105 | ||
22c3cf95 OZ |
106 | static int |
107 | rip_send_ack(struct rip_proto *p, struct rip_iface *ifa, uint flush, uint seqnum); | |
8465dccb OZ |
108 | |
109 | #define DROP(DSC,VAL) do { err_dsc = DSC; err_val = VAL; goto drop; } while(0) | |
110 | #define DROP1(DSC) do { err_dsc = DSC; goto drop; } while(0) | |
111 | #define SKIP(DSC) do { err_dsc = DSC; goto skip; } while(0) | |
112 | ||
113 | #define LOG_PKT(msg, args...) \ | |
114 | log_rl(&p->log_pkt_tbf, L_REMOTE "%s: " msg, p->p.name, args) | |
115 | ||
116 | #define LOG_PKT_AUTH(msg, args...) \ | |
117 | log_rl(&p->log_pkt_tbf, L_AUTH "%s: " msg, p->p.name, args) | |
118 | ||
119 | #define LOG_RTE(msg, args...) \ | |
120 | log_rl(&p->log_rte_tbf, L_REMOTE "%s: " msg, p->p.name, args) | |
121 | ||
122 | ||
123 | static inline void * rip_tx_buffer(struct rip_iface *ifa) | |
124 | { return ifa->sk->tbuf; } | |
125 | ||
22c3cf95 OZ |
126 | static inline uint |
127 | rip_pkt_hdrlen(struct rip_iface *ifa) | |
128 | { | |
129 | return sizeof(struct rip_packet) + | |
130 | (ifa->cf->demand_circuit ? sizeof(struct rip_update_hdr) : 0) + | |
131 | (ifa->cf->auth_type ? RIP_BLOCK_LENGTH : 0); | |
132 | } | |
133 | ||
134 | static inline struct rip_update_hdr * | |
135 | rip_get_update_hdr(struct rip_packet *pkt) | |
136 | { | |
137 | return (void *) ((byte *) pkt + sizeof(struct rip_packet)); | |
138 | } | |
139 | ||
140 | static inline struct rip_block_auth * | |
141 | rip_get_auth_block(struct rip_iface *ifa, struct rip_packet *pkt) | |
142 | { | |
143 | return (void *) ((byte *) pkt + sizeof(struct rip_packet) + | |
144 | (ifa->cf->demand_circuit ? sizeof(struct rip_update_hdr) : 0)); | |
145 | } | |
146 | ||
8465dccb OZ |
147 | |
148 | static inline void | |
149 | rip_put_block(struct rip_proto *p, byte *pos, struct rip_block *rte) | |
150 | { | |
151 | if (rip_is_v2(p)) | |
152 | { | |
153 | struct rip_block_v2 *block = (void *) pos; | |
154 | block->family = rte->no_af ? 0 : htons(RIP_AF_IPV4); | |
155 | block->tag = htons(rte->tag); | |
fe9f1a6d OZ |
156 | block->network = ip4_hton(net4_prefix(&rte->net)); |
157 | block->netmask = ip4_hton(ip4_mkmask(net4_pxlen(&rte->net))); | |
8465dccb OZ |
158 | block->next_hop = ip4_hton(ipa_to_ip4(rte->next_hop)); |
159 | block->metric = htonl(rte->metric); | |
160 | } | |
161 | else /* RIPng */ | |
162 | { | |
163 | struct rip_block_ng *block = (void *) pos; | |
fe9f1a6d | 164 | block->prefix = ip6_hton(net6_prefix(&rte->net)); |
8465dccb | 165 | block->tag = htons(rte->tag); |
fe9f1a6d | 166 | block->pxlen = net6_pxlen(&rte->net); |
8465dccb OZ |
167 | block->metric = rte->metric; |
168 | } | |
169 | } | |
170 | ||
171 | static inline void | |
8860e991 | 172 | rip_put_next_hop(struct rip_proto *p UNUSED, byte *pos, struct rip_block *rte) |
8465dccb OZ |
173 | { |
174 | struct rip_block_ng *block = (void *) pos; | |
175 | block->prefix = ip6_hton(ipa_to_ip6(rte->next_hop)); | |
176 | block->tag = 0; | |
177 | block->pxlen = 0; | |
178 | block->metric = 0xff; | |
179 | } | |
180 | ||
181 | static inline int | |
182 | rip_get_block(struct rip_proto *p, byte *pos, struct rip_block *rte) | |
183 | { | |
184 | if (rip_is_v2(p)) | |
185 | { | |
186 | struct rip_block_v2 *block = (void *) pos; | |
187 | ||
188 | /* Skip blocks with strange AF, including authentication blocks */ | |
189 | if (block->family != (rte->no_af ? 0 : htons(RIP_AF_IPV4))) | |
190 | return 0; | |
191 | ||
fe9f1a6d OZ |
192 | uint pxlen = ip4_masklen(ip4_ntoh(block->netmask)); |
193 | net_fill_ip4(&rte->net, ip4_ntoh(block->network), pxlen); | |
8465dccb OZ |
194 | rte->metric = ntohl(block->metric); |
195 | rte->tag = ntohs(block->tag); | |
196 | rte->next_hop = ipa_from_ip4(ip4_ntoh(block->next_hop)); | |
197 | ||
198 | return 1; | |
199 | } | |
200 | else /* RIPng */ | |
201 | { | |
202 | struct rip_block_ng *block = (void *) pos; | |
203 | ||
204 | /* Handle and skip next hop blocks */ | |
205 | if (block->metric == 0xff) | |
206 | { | |
207 | rte->next_hop = ipa_from_ip6(ip6_ntoh(block->prefix)); | |
208 | if (!ipa_is_link_local(rte->next_hop)) rte->next_hop = IPA_NONE; | |
209 | return 0; | |
210 | } | |
211 | ||
74d94167 | 212 | uint pxlen = (block->pxlen <= IP6_MAX_PREFIX_LENGTH) ? block->pxlen : 255; |
fe9f1a6d | 213 | net_fill_ip6(&rte->net, ip6_ntoh(block->prefix), pxlen); |
8465dccb OZ |
214 | rte->metric = block->metric; |
215 | rte->tag = ntohs(block->tag); | |
216 | /* rte->next_hop is deliberately kept unmodified */; | |
217 | ||
218 | return 1; | |
219 | } | |
220 | } | |
221 | ||
222 | static inline void | |
223 | rip_update_csn(struct rip_proto *p UNUSED, struct rip_iface *ifa) | |
224 | { | |
225 | /* | |
226 | * We update crypto sequence numbers at the beginning of update session to | |
227 | * avoid issues with packet reordering, so packets inside one update session | |
228 | * have the same CSN. We are using real time, but enforcing monotonicity. | |
229 | */ | |
230 | if (ifa->cf->auth_type == RIP_AUTH_CRYPTO) | |
574b2324 OZ |
231 | { |
232 | u32 now_real = (u32) (current_real_time() TO_S); | |
233 | ifa->csn = (ifa->csn < now_real) ? now_real : ifa->csn + 1; | |
234 | } | |
8465dccb OZ |
235 | } |
236 | ||
22c3cf95 OZ |
237 | static inline void |
238 | rip_fill_header(struct rip_iface *ifa, struct rip_packet *pkt, uint cmd) | |
239 | { | |
240 | *pkt = (struct rip_packet) { | |
241 | .command = cmd, | |
242 | .version = ifa->cf->version | |
243 | }; | |
244 | } | |
245 | ||
246 | static inline void | |
247 | rip_fill_update_hdr(struct rip_packet *pkt, uint flush, uint seqnum) | |
248 | { | |
249 | struct rip_update_hdr *hdr = rip_get_update_hdr(pkt); | |
250 | ||
251 | *hdr = (struct rip_update_hdr) { | |
252 | .version = RIP_UPDATE_VERSION, | |
253 | .flush = flush, | |
254 | .seqnum = htons(seqnum) | |
255 | }; | |
256 | } | |
257 | ||
8465dccb OZ |
258 | static void |
259 | rip_fill_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen) | |
260 | { | |
22c3cf95 | 261 | struct rip_block_auth *auth = rip_get_auth_block(ifa, pkt); |
8465dccb OZ |
262 | struct password_item *pass = password_find(ifa->cf->passwords, 0); |
263 | ||
264 | if (!pass) | |
265 | { | |
266 | /* FIXME: This should not happen */ | |
267 | log(L_ERR "%s: No suitable password found for authentication", p->p.name); | |
268 | memset(auth, 0, sizeof(struct rip_block_auth)); | |
269 | return; | |
270 | } | |
271 | ||
272 | switch (ifa->cf->auth_type) | |
273 | { | |
274 | case RIP_AUTH_PLAIN: | |
275 | auth->must_be_ffff = htons(0xffff); | |
276 | auth->auth_type = htons(RIP_AUTH_PLAIN); | |
277 | strncpy(auth->password, pass->password, RIP_PASSWD_LENGTH); | |
278 | return; | |
279 | ||
280 | case RIP_AUTH_CRYPTO: | |
281 | auth->must_be_ffff = htons(0xffff); | |
282 | auth->auth_type = htons(RIP_AUTH_CRYPTO); | |
283 | auth->packet_len = htons(*plen); | |
284 | auth->key_id = pass->id; | |
390601f0 | 285 | auth->auth_len = mac_type_length(pass->alg); |
8465dccb OZ |
286 | auth->seq_num = ifa->csn_ready ? htonl(ifa->csn) : 0; |
287 | auth->unused1 = 0; | |
288 | auth->unused2 = 0; | |
289 | ifa->csn_ready = 1; | |
290 | ||
390601f0 OZ |
291 | if (pass->alg < ALG_HMAC) |
292 | auth->auth_len += sizeof(struct rip_auth_tail); | |
293 | ||
8465dccb OZ |
294 | /* |
295 | * Note that RFC 4822 is unclear whether auth_len should cover whole | |
296 | * authentication trailer or just auth_data length. | |
297 | * | |
390601f0 OZ |
298 | * FIXME: We should use just auth_data length by default. Currently we put |
299 | * the whole auth trailer length in keyed hash case to keep old behavior, | |
300 | * but we put just auth_data length in the new HMAC case. Note that Quagga | |
301 | * has config option for this. | |
302 | * | |
8465dccb OZ |
303 | * Crypto sequence numbers are increased by sender in rip_update_csn(). |
304 | * First CSN should be zero, this is handled by csn_ready. | |
305 | */ | |
306 | ||
307 | struct rip_auth_tail *tail = (void *) ((byte *) pkt + *plen); | |
308 | tail->must_be_ffff = htons(0xffff); | |
309 | tail->must_be_0001 = htons(0x0001); | |
8465dccb | 310 | |
390601f0 OZ |
311 | uint auth_len = mac_type_length(pass->alg); |
312 | *plen += sizeof(struct rip_auth_tail) + auth_len; | |
8465dccb | 313 | |
390601f0 OZ |
314 | /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ |
315 | if (pass->alg < ALG_HMAC) | |
316 | strncpy(tail->auth_data, pass->password, auth_len); | |
317 | else | |
318 | memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); | |
319 | ||
320 | mac_fill(pass->alg, pass->password, pass->length, | |
321 | (byte *) pkt, *plen, tail->auth_data); | |
8465dccb OZ |
322 | return; |
323 | ||
324 | default: | |
325 | bug("Unknown authentication type"); | |
326 | } | |
327 | } | |
328 | ||
329 | static int | |
22c3cf95 | 330 | rip_check_authentication(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint *plen, uint hdr_len, struct rip_neighbor *n) |
8465dccb | 331 | { |
22c3cf95 | 332 | struct rip_block_auth *auth = (void *) ((byte *) pkt + hdr_len); |
8465dccb OZ |
333 | struct password_item *pass = NULL; |
334 | const char *err_dsc = NULL; | |
335 | uint err_val = 0; | |
336 | uint auth_type = 0; | |
337 | ||
338 | /* Check for authentication entry */ | |
22c3cf95 | 339 | if ((*plen >= (hdr_len + sizeof(struct rip_block_auth))) && |
8465dccb OZ |
340 | (auth->must_be_ffff == htons(0xffff))) |
341 | auth_type = ntohs(auth->auth_type); | |
342 | ||
343 | if (auth_type != ifa->cf->auth_type) | |
344 | DROP("authentication method mismatch", auth_type); | |
345 | ||
346 | switch (auth_type) | |
347 | { | |
348 | case RIP_AUTH_NONE: | |
349 | return 1; | |
350 | ||
351 | case RIP_AUTH_PLAIN: | |
352 | pass = password_find_by_value(ifa->cf->passwords, auth->password, RIP_PASSWD_LENGTH); | |
353 | if (!pass) | |
354 | DROP1("wrong password"); | |
355 | ||
356 | return 1; | |
357 | ||
358 | case RIP_AUTH_CRYPTO: | |
359 | pass = password_find_by_id(ifa->cf->passwords, auth->key_id); | |
360 | if (!pass) | |
361 | DROP("no suitable password found", auth->key_id); | |
362 | ||
363 | uint data_len = ntohs(auth->packet_len); | |
390601f0 OZ |
364 | uint auth_len = mac_type_length(pass->alg); |
365 | uint auth_len2 = sizeof(struct rip_auth_tail) + auth_len; | |
8465dccb | 366 | |
390601f0 OZ |
367 | /* |
368 | * Ideally, first check should be check for internal consistency: | |
369 | * (data_len + sizeof(struct rip_auth_tail) + auth->auth_len) != *plen | |
370 | * | |
371 | * Second one should check expected code length: | |
372 | * auth->auth_len != auth_len | |
373 | * | |
374 | * But as auth->auth_len has two interpretations, we simplify this | |
375 | */ | |
8465dccb | 376 | |
390601f0 OZ |
377 | if (data_len + auth_len2 != *plen) |
378 | DROP("packet length mismatch", *plen); | |
8465dccb | 379 | |
390601f0 OZ |
380 | /* Warning: two interpretations of auth_len field */ |
381 | if ((auth->auth_len != auth_len) && (auth->auth_len != auth_len2)) | |
382 | DROP("wrong authentication length", auth->auth_len); | |
8465dccb OZ |
383 | |
384 | struct rip_auth_tail *tail = (void *) ((byte *) pkt + data_len); | |
385 | if ((tail->must_be_ffff != htons(0xffff)) || (tail->must_be_0001 != htons(0x0001))) | |
386 | DROP1("authentication trailer is missing"); | |
387 | ||
388 | /* Accept higher sequence number, or zero if connectivity is lost */ | |
389 | /* FIXME: sequence number must be password/SA specific */ | |
390 | u32 rcv_csn = ntohl(auth->seq_num); | |
391 | if ((rcv_csn < n->csn) && (rcv_csn || n->uc)) | |
392 | { | |
393 | /* We want to report both new and old CSN */ | |
394 | LOG_PKT_AUTH("Authentication failed for %I on %s - " | |
395 | "lower sequence number (rcv %u, old %u)", | |
396 | n->nbr->addr, ifa->iface->name, rcv_csn, n->csn); | |
397 | return 0; | |
398 | } | |
399 | ||
390601f0 OZ |
400 | byte *auth_data = alloca(auth_len); |
401 | memcpy(auth_data, tail->auth_data, auth_len); | |
8465dccb | 402 | |
390601f0 OZ |
403 | /* Append key for keyed hash, append padding for HMAC (RFC 4822 2.5) */ |
404 | if (pass->alg < ALG_HMAC) | |
405 | strncpy(tail->auth_data, pass->password, auth_len); | |
406 | else | |
407 | memset32(tail->auth_data, HMAC_MAGIC, auth_len / 4); | |
8465dccb | 408 | |
390601f0 OZ |
409 | if (!mac_verify(pass->alg, pass->password, pass->length, |
410 | (byte *) pkt, *plen, auth_data)) | |
411 | DROP("wrong authentication code", pass->id); | |
8465dccb OZ |
412 | |
413 | *plen = data_len; | |
414 | n->csn = rcv_csn; | |
415 | ||
416 | return 1; | |
417 | } | |
418 | ||
419 | drop: | |
420 | LOG_PKT_AUTH("Authentication failed for %I on %s - %s (%u)", | |
421 | n->nbr->addr, ifa->iface->name, err_dsc, err_val); | |
422 | ||
423 | return 0; | |
424 | } | |
425 | ||
426 | static inline int | |
427 | rip_send_to(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, ip_addr dst) | |
428 | { | |
429 | if (ifa->cf->auth_type) | |
430 | rip_fill_authentication(p, ifa, pkt, &plen); | |
431 | ||
432 | return sk_send_to(ifa->sk, plen, dst, 0); | |
433 | } | |
434 | ||
22c3cf95 OZ |
435 | static inline void |
436 | rip_kick_rxmt_timer(struct rip_iface *ifa) | |
437 | { | |
438 | if (! tm_active(ifa->rxmt_timer)) | |
439 | tm_start(ifa->rxmt_timer, ifa->cf->rxmt_time); | |
440 | } | |
441 | ||
8465dccb OZ |
442 | |
443 | void | |
444 | rip_send_request(struct rip_proto *p, struct rip_iface *ifa) | |
445 | { | |
22c3cf95 OZ |
446 | struct rip_packet *pkt = rip_tx_buffer(ifa); |
447 | byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); | |
448 | ||
449 | rip_fill_header(ifa, pkt, RIP_CMD_REQUEST); | |
450 | ||
451 | if (ifa->cf->demand_circuit) | |
452 | { | |
453 | pkt->command = RIP_CMD_UPDATE_REQUEST; | |
454 | rip_fill_update_hdr(pkt, 0, 0); | |
8465dccb | 455 | |
22c3cf95 OZ |
456 | /* Must be acknowledged by update response */ |
457 | ifa->req_pending = 1; | |
458 | rip_kick_rxmt_timer(ifa); | |
459 | } | |
8465dccb OZ |
460 | |
461 | struct rip_block b = { .no_af = 1, .metric = p->infinity }; | |
462 | rip_put_block(p, pos, &b); | |
463 | pos += RIP_BLOCK_LENGTH; | |
464 | ||
465 | rip_update_csn(p, ifa); | |
466 | ||
467 | TRACE(D_PACKETS, "Sending request via %s", ifa->iface->name); | |
468 | rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->addr); | |
469 | } | |
470 | ||
471 | static void | |
472 | rip_receive_request(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) | |
473 | { | |
474 | TRACE(D_PACKETS, "Request received from %I on %s", from->nbr->addr, ifa->iface->name); | |
475 | ||
476 | byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); | |
477 | ||
478 | /* We expect one regular block */ | |
479 | if (plen != (rip_pkt_hdrlen(ifa) + RIP_BLOCK_LENGTH)) | |
480 | return; | |
481 | ||
482 | struct rip_block b = { .no_af = 1 }; | |
483 | ||
484 | if (!rip_get_block(p, pos, &b)) | |
485 | return; | |
486 | ||
fe9f1a6d OZ |
487 | /* Special case - infinity metric, for RIPng also zero prefix */ |
488 | if ((b.metric != p->infinity) || | |
489 | (rip_is_ng(p) && !net_zero_ip6((net_addr_ip6 *) &b.net))) | |
8465dccb OZ |
490 | return; |
491 | ||
492 | /* We do nothing if TX is already active */ | |
493 | if (ifa->tx_active) | |
494 | { | |
495 | TRACE(D_EVENTS, "Skipping request from %I on %s, TX is busy", from->nbr->addr, ifa->iface->name); | |
496 | return; | |
497 | } | |
498 | ||
499 | if (!ifa->cf->passive) | |
500 | rip_send_table(p, ifa, from->nbr->addr, 0); | |
501 | } | |
502 | ||
503 | ||
504 | static int | |
505 | rip_send_response(struct rip_proto *p, struct rip_iface *ifa) | |
506 | { | |
507 | if (! ifa->tx_active) | |
508 | return 0; | |
509 | ||
22c3cf95 OZ |
510 | /* In demand circuit mode, we may wait for ACK */ |
511 | if (ifa->tx_pending) | |
512 | return 0; | |
513 | ||
514 | struct rip_packet *pkt = rip_tx_buffer(ifa); | |
515 | byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); | |
516 | byte *max = (byte *) pkt + ifa->tx_plen - | |
8465dccb OZ |
517 | (rip_is_v2(p) ? RIP_BLOCK_LENGTH : 2*RIP_BLOCK_LENGTH); |
518 | ip_addr last_next_hop = IPA_NONE; | |
92cc1e74 | 519 | btime now_ = current_time(); |
8465dccb OZ |
520 | int send = 0; |
521 | ||
22c3cf95 OZ |
522 | rip_fill_header(ifa, pkt, RIP_CMD_RESPONSE); |
523 | ||
524 | if (ifa->cf->demand_circuit) | |
525 | { | |
526 | pkt->command = RIP_CMD_UPDATE_RESPONSE; | |
527 | rip_fill_update_hdr(pkt, ifa->tx_flush, ifa->tx_seqnum); | |
528 | } | |
8465dccb | 529 | |
600998fc | 530 | FIB_ITERATE_START(&p->rtable, &ifa->tx_fit, struct rip_entry, en) |
8465dccb | 531 | { |
8465dccb OZ |
532 | /* Dummy entries */ |
533 | if (!en->valid) | |
534 | goto next_entry; | |
535 | ||
536 | /* Stale entries that should be removed */ | |
537 | if ((en->valid == RIP_ENTRY_STALE) && | |
92cc1e74 | 538 | ((en->changed + ifa->cf->garbage_time) <= now_)) |
8465dccb OZ |
539 | goto next_entry; |
540 | ||
541 | /* Triggered updates */ | |
542 | if (en->changed < ifa->tx_changed) | |
543 | goto next_entry; | |
544 | ||
545 | /* Not enough space for current entry */ | |
546 | if (pos > max) | |
547 | { | |
600998fc | 548 | FIB_ITERATE_PUT(&ifa->tx_fit); |
22c3cf95 | 549 | goto send_pkt; |
8465dccb OZ |
550 | } |
551 | ||
552 | struct rip_block rte = { | |
8465dccb OZ |
553 | .metric = en->metric, |
554 | .tag = en->tag | |
555 | }; | |
556 | ||
fe9f1a6d OZ |
557 | net_copy(&rte.net, en->n.addr); |
558 | ||
8465dccb OZ |
559 | if (en->iface == ifa->iface) |
560 | rte.next_hop = en->next_hop; | |
561 | ||
562 | if (rip_is_v2(p) && (ifa->cf->version == RIP_V1)) | |
563 | { | |
564 | /* Skipping subnets (i.e. not hosts, classful networks or default route) */ | |
fe9f1a6d | 565 | if (ip4_masklen(ip4_class_mask(net4_prefix(&rte.net))) != rte.net.pxlen) |
8465dccb OZ |
566 | goto next_entry; |
567 | ||
568 | rte.tag = 0; | |
fe9f1a6d | 569 | rte.net.pxlen = 0; |
8465dccb OZ |
570 | rte.next_hop = IPA_NONE; |
571 | } | |
572 | ||
573 | /* Split horizon */ | |
574 | if (en->from == ifa->iface && ifa->cf->split_horizon) | |
575 | { | |
576 | if (ifa->cf->poison_reverse) | |
577 | { | |
578 | rte.metric = p->infinity; | |
579 | rte.next_hop = IPA_NONE; | |
580 | } | |
581 | else | |
582 | goto next_entry; | |
583 | } | |
584 | ||
fe9f1a6d | 585 | // TRACE(D_PACKETS, " %N -> %I metric %d", &rte.net, rte.next_hop, rte.metric); |
8465dccb OZ |
586 | |
587 | /* RIPng next hop entry */ | |
588 | if (rip_is_ng(p) && !ipa_equal(rte.next_hop, last_next_hop)) | |
589 | { | |
590 | last_next_hop = rte.next_hop; | |
591 | rip_put_next_hop(p, pos, &rte); | |
592 | pos += RIP_BLOCK_LENGTH; | |
593 | } | |
594 | ||
595 | rip_put_block(p, pos, &rte); | |
596 | pos += RIP_BLOCK_LENGTH; | |
597 | send = 1; | |
598 | ||
599 | next_entry: ; | |
600 | } | |
600998fc | 601 | FIB_ITERATE_END; |
22c3cf95 OZ |
602 | |
603 | if (send) | |
604 | { | |
605 | FIB_ITERATE_PUT_END(&ifa->tx_fit); | |
606 | goto send_pkt; | |
607 | } | |
8465dccb OZ |
608 | |
609 | /* Do not send empty packet */ | |
8465dccb | 610 | |
22c3cf95 OZ |
611 | ifa->tx_active = 0; |
612 | ||
613 | /* Unlink second iterator */ | |
614 | FIB_ITERATE_UNLINK(&ifa->tx_done, &p->rtable); | |
615 | ||
616 | return 0; | |
617 | ||
618 | send_pkt: | |
619 | ||
620 | /* Waiting for ack or timeout */ | |
621 | if (ifa->cf->demand_circuit) | |
622 | { | |
623 | ifa->tx_pending = 1; | |
624 | rip_kick_rxmt_timer(ifa); | |
625 | } | |
626 | ||
8465dccb OZ |
627 | TRACE(D_PACKETS, "Sending response via %s", ifa->iface->name); |
628 | return rip_send_to(p, ifa, pkt, pos - (byte *) pkt, ifa->tx_addr); | |
629 | } | |
630 | ||
631 | /** | |
632 | * rip_send_table - RIP interface timer hook | |
633 | * @p: RIP instance | |
634 | * @ifa: RIP interface | |
635 | * @addr: destination IP address | |
636 | * @changed: time limit for triggered updates | |
637 | * | |
638 | * The function activates an update session and starts sending routing update | |
639 | * packets (using rip_send_response()). The session may be finished during the | |
640 | * call or may continue in rip_tx_hook() until all appropriate routes are | |
641 | * transmitted. Note that there may be at most one active update session per | |
642 | * interface, the function will terminate the old active session before | |
643 | * activating the new one. | |
644 | */ | |
645 | void | |
92cc1e74 | 646 | rip_send_table(struct rip_proto *p, struct rip_iface *ifa, ip_addr addr, btime changed) |
8465dccb | 647 | { |
d82987d1 | 648 | DBG("RIP: Opening TX session to %I on %s\n", addr, ifa->iface->name); |
8465dccb OZ |
649 | |
650 | rip_reset_tx_session(p, ifa); | |
651 | ||
652 | ifa->tx_active = 1; | |
653 | ifa->tx_addr = addr; | |
654 | ifa->tx_changed = changed; | |
655 | FIB_ITERATE_INIT(&ifa->tx_fit, &p->rtable); | |
22c3cf95 OZ |
656 | FIB_ITERATE_INIT(&ifa->tx_done, &p->rtable); |
657 | ||
658 | if (ifa->cf->demand_circuit) | |
659 | ifa->tx_flush = ! changed; | |
8465dccb OZ |
660 | |
661 | rip_update_csn(p, ifa); | |
662 | ||
663 | while (rip_send_response(p, ifa) > 0) | |
664 | ; | |
665 | } | |
666 | ||
667 | static void | |
668 | rip_tx_hook(sock *sk) | |
669 | { | |
670 | struct rip_iface *ifa = sk->data; | |
671 | struct rip_proto *p = ifa->rip; | |
672 | ||
673 | DBG("RIP: TX hook called (iface %s, src %I, dst %I)\n", | |
674 | sk->iface->name, sk->saddr, sk->daddr); | |
675 | ||
676 | while (rip_send_response(p, ifa) > 0) | |
677 | ; | |
678 | } | |
679 | ||
680 | static void | |
681 | rip_err_hook(sock *sk, int err) | |
682 | { | |
683 | struct rip_iface *ifa = sk->data; | |
684 | struct rip_proto *p = ifa->rip; | |
685 | ||
686 | log(L_ERR "%s: Socket error on %s: %M", p->p.name, ifa->iface->name, err); | |
687 | ||
688 | rip_reset_tx_session(p, ifa); | |
689 | } | |
690 | ||
691 | static void | |
692 | rip_receive_response(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen, struct rip_neighbor *from) | |
693 | { | |
694 | struct rip_block rte = {}; | |
695 | const char *err_dsc = NULL; | |
696 | ||
697 | TRACE(D_PACKETS, "Response received from %I on %s", from->nbr->addr, ifa->iface->name); | |
698 | ||
22c3cf95 | 699 | byte *pos = (byte *) pkt + rip_pkt_hdrlen(ifa); |
8465dccb | 700 | byte *end = (byte *) pkt + plen; |
22c3cf95 OZ |
701 | |
702 | btime expires = current_time() + ifa->cf->timeout_time; | |
703 | ||
704 | if (pkt->command == RIP_CMD_UPDATE_RESPONSE) | |
705 | { | |
706 | struct rip_update_hdr *hdr = rip_get_update_hdr(pkt); | |
707 | rip_send_ack(p, ifa, hdr->flush, ntohs(hdr->seqnum)); | |
708 | expires = TIME_INFINITY; | |
709 | ||
710 | /* Handle flush bit */ | |
711 | if (hdr->flush) | |
712 | { | |
713 | /* Flush old routes */ | |
714 | rip_flush_table(p, from); | |
715 | ||
716 | /* Acknowledge pending request */ | |
717 | ifa->req_pending = 0; | |
718 | } | |
719 | } | |
8465dccb OZ |
720 | |
721 | for (; pos < end; pos += RIP_BLOCK_LENGTH) | |
722 | { | |
723 | /* Find next regular RTE */ | |
724 | if (!rip_get_block(p, pos, &rte)) | |
725 | continue; | |
726 | ||
8465dccb OZ |
727 | if (rip_is_v2(p) && (pkt->version == RIP_V1)) |
728 | { | |
fe9f1a6d | 729 | if (ifa->cf->check_zero && (rte.tag || rte.net.pxlen || ipa_nonzero(rte.next_hop))) |
8465dccb OZ |
730 | SKIP("RIPv1 reserved field is nonzero"); |
731 | ||
732 | rte.tag = 0; | |
fe9f1a6d | 733 | rte.net.pxlen = ip4_masklen(ip4_class_mask(net4_prefix(&rte.net))); |
8465dccb OZ |
734 | rte.next_hop = IPA_NONE; |
735 | } | |
736 | ||
fe9f1a6d | 737 | if (rte.net.pxlen == 255) |
8465dccb OZ |
738 | SKIP("invalid prefix length"); |
739 | ||
fe9f1a6d OZ |
740 | net_normalize(&rte.net); |
741 | ||
742 | int c = net_classify(&rte.net); | |
743 | if ((c < 0) || !(c & IADDR_HOST) || ((c & IADDR_SCOPE_MASK) <= SCOPE_LINK)) | |
744 | SKIP("invalid prefix"); | |
745 | ||
8465dccb OZ |
746 | if (rte.metric > p->infinity) |
747 | SKIP("invalid metric"); | |
748 | ||
749 | if (ipa_nonzero(rte.next_hop)) | |
750 | { | |
586c1800 | 751 | neighbor *nbr = neigh_find(&p->p, rte.next_hop, ifa->iface, 0); |
8465dccb OZ |
752 | if (!nbr || (nbr->scope <= 0)) |
753 | rte.next_hop = IPA_NONE; | |
754 | } | |
755 | ||
fe9f1a6d | 756 | // TRACE(D_PACKETS, " %N -> %I metric %d", &rte.net.n, rte.next_hop, rte.metric); |
8465dccb OZ |
757 | |
758 | rte.metric += ifa->cf->metric; | |
759 | ||
760 | if (rte.metric < p->infinity) | |
761 | { | |
762 | struct rip_rte new = { | |
763 | .from = from, | |
764 | .next_hop = ipa_nonzero(rte.next_hop) ? rte.next_hop : from->nbr->addr, | |
765 | .metric = rte.metric, | |
766 | .tag = rte.tag, | |
22c3cf95 | 767 | .expires = expires |
8465dccb OZ |
768 | }; |
769 | ||
fe9f1a6d | 770 | rip_update_rte(p, &rte.net, &new); |
8465dccb OZ |
771 | } |
772 | else | |
fe9f1a6d | 773 | rip_withdraw_rte(p, &rte.net, from); |
8465dccb OZ |
774 | |
775 | continue; | |
776 | ||
777 | skip: | |
fe9f1a6d OZ |
778 | LOG_RTE("Ignoring route %N received from %I - %s", |
779 | &rte.net, from->nbr->addr, err_dsc); | |
8465dccb OZ |
780 | } |
781 | } | |
782 | ||
22c3cf95 OZ |
783 | |
784 | static int | |
785 | rip_send_ack(struct rip_proto *p, struct rip_iface *ifa, uint flush, uint seqnum) | |
786 | { | |
787 | struct rip_packet *pkt = rip_tx_buffer(ifa); | |
788 | ||
789 | rip_fill_header(ifa, pkt, RIP_CMD_UPDATE_ACK); | |
790 | rip_fill_update_hdr(pkt, flush, seqnum); | |
791 | ||
792 | TRACE(D_PACKETS, "Sending acknowledge via %s", ifa->iface->name); | |
793 | return rip_send_to(p, ifa, pkt, rip_pkt_hdrlen(ifa), ifa->tx_addr); | |
794 | } | |
795 | ||
796 | static void | |
797 | rip_receive_ack(struct rip_proto *p, struct rip_iface *ifa, struct rip_packet *pkt, uint plen UNUSED, struct rip_neighbor *from) | |
798 | { | |
799 | TRACE(D_PACKETS, "Acknowledge received from %I on %s", from->nbr->addr, ifa->iface->name); | |
800 | ||
801 | struct rip_update_hdr *hdr = rip_get_update_hdr(pkt); | |
802 | uint seqnum = ntohs(hdr->seqnum); | |
803 | ||
804 | if (! ifa->tx_active || ! ifa->tx_pending) | |
805 | { | |
806 | LOG_PKT("Bad acknowledge packet from %I via %s - no pending response", | |
807 | from->nbr->addr, ifa->iface->name); | |
808 | return; | |
809 | } | |
810 | ||
811 | if (seqnum != ifa->tx_seqnum) | |
812 | { | |
813 | LOG_PKT("Bad acknowledge packet from %I via %s - " | |
814 | "mismatched sequence number (rcv %u, old %u)", | |
815 | from->nbr->addr, ifa->iface->name, seqnum, (uint) ifa->tx_seqnum); | |
816 | return; | |
817 | } | |
818 | ||
819 | /* Move acked position */ | |
820 | FIB_ITERATE_COPY(&ifa->tx_done, &ifa->tx_fit, &p->rtable); | |
821 | ||
822 | /* Packet is no longer pending */ | |
823 | ifa->tx_pending = 0; | |
824 | ifa->tx_seqnum++; | |
825 | ||
826 | /* Next one does not have flush bit */ | |
827 | ifa->tx_flush = 0; | |
828 | ||
829 | rip_send_response(p, ifa); | |
830 | } | |
831 | ||
832 | /** | |
833 | * rip_rxmt_timeout - RIP retransmission timer hook | |
834 | * @t: timer | |
835 | * | |
836 | * In Demand Circuit mode, update packets must be acknowledged to ensure | |
837 | * reliability. If they are not acknowledged, we need to retransmit them. | |
838 | */ | |
839 | void | |
840 | rip_rxmt_timeout(timer *t) | |
841 | { | |
842 | struct rip_iface *ifa = t->data; | |
843 | struct rip_proto *p = ifa->rip; | |
844 | ||
845 | if (ifa->req_pending) | |
846 | rip_send_request(p, ifa); | |
847 | ||
848 | if (ifa->tx_pending) | |
849 | { | |
850 | /* Revert to acked position */ | |
851 | FIB_ITERATE_COPY(&ifa->tx_fit, &ifa->tx_done, &p->rtable); | |
852 | ||
853 | /* Packet is no longer pending */ | |
854 | ifa->tx_pending = 0; | |
855 | ifa->tx_seqnum++; | |
856 | ||
857 | rip_send_response(p, ifa); | |
858 | } | |
859 | } | |
860 | ||
861 | ||
8465dccb | 862 | static int |
3e236955 | 863 | rip_rx_hook(sock *sk, uint len) |
8465dccb OZ |
864 | { |
865 | struct rip_iface *ifa = sk->data; | |
866 | struct rip_proto *p = ifa->rip; | |
867 | const char *err_dsc = NULL; | |
868 | uint err_val = 0; | |
869 | ||
870 | if (sk->lifindex != sk->iface->index) | |
871 | return 1; | |
872 | ||
873 | DBG("RIP: RX hook called (iface %s, src %I, dst %I)\n", | |
874 | sk->iface->name, sk->faddr, sk->laddr); | |
875 | ||
876 | /* Silently ignore my own packets */ | |
153f02da | 877 | if (ipa_equal(sk->faddr, sk->saddr)) |
8465dccb OZ |
878 | return 1; |
879 | ||
880 | if (rip_is_ng(p) && !ipa_is_link_local(sk->faddr)) | |
881 | DROP1("wrong src address"); | |
882 | ||
883 | struct rip_neighbor *n = rip_get_neighbor(p, &sk->faddr, ifa); | |
884 | ||
885 | if (!n) | |
886 | DROP1("not from neighbor"); | |
887 | ||
888 | if ((ifa->cf->ttl_security == 1) && (sk->rcv_ttl < 255)) | |
889 | DROP("wrong TTL", sk->rcv_ttl); | |
890 | ||
891 | if (sk->fport != sk->dport) | |
892 | DROP("wrong src port", sk->fport); | |
893 | ||
894 | if (len < sizeof(struct rip_packet)) | |
895 | DROP("too short", len); | |
896 | ||
897 | if (sk->flags & SKF_TRUNCATED) | |
898 | DROP("truncated", len); | |
899 | ||
900 | struct rip_packet *pkt = (struct rip_packet *) sk->rbuf; | |
22c3cf95 OZ |
901 | uint pkt_len = len; |
902 | uint hdr_len = sizeof(struct rip_packet); | |
903 | uint update_msg = 0; | |
8465dccb OZ |
904 | |
905 | if (!pkt->version || (ifa->cf->version_only && (pkt->version != ifa->cf->version))) | |
906 | DROP("wrong version", pkt->version); | |
907 | ||
22c3cf95 OZ |
908 | /* Update packets (RFC 2091) have additional header even before auth data */ |
909 | if ((pkt->command == RIP_CMD_UPDATE_REQUEST) || | |
910 | (pkt->command == RIP_CMD_UPDATE_RESPONSE) || | |
911 | (pkt->command == RIP_CMD_UPDATE_ACK)) | |
912 | { | |
913 | hdr_len += sizeof(struct rip_update_hdr); | |
914 | ||
915 | if (len < hdr_len) | |
916 | DROP("too short", len); | |
917 | ||
918 | struct rip_update_hdr *hdr = rip_get_update_hdr(pkt); | |
919 | ||
920 | if (hdr->version != RIP_UPDATE_VERSION) | |
921 | DROP("wrong update header version", hdr->version); | |
922 | ||
923 | if (hdr->flush > 1) | |
924 | DROP("wrong flush value", hdr->flush); | |
925 | ||
926 | update_msg = 1; | |
927 | } | |
928 | ||
8465dccb | 929 | /* rip_check_authentication() has its own error logging */ |
22c3cf95 | 930 | if (rip_is_v2(p) && !rip_check_authentication(p, ifa, pkt, &pkt_len, hdr_len, n)) |
8465dccb OZ |
931 | return 1; |
932 | ||
22c3cf95 OZ |
933 | if ((pkt_len - hdr_len) % RIP_BLOCK_LENGTH) |
934 | DROP("invalid length", pkt_len); | |
935 | ||
936 | if (update_msg != ifa->cf->demand_circuit) | |
937 | DROP("demand circuit mode mismatch", update_msg); | |
8465dccb | 938 | |
92cc1e74 | 939 | n->last_seen = current_time(); |
8465dccb OZ |
940 | rip_update_bfd(p, n); |
941 | ||
942 | switch (pkt->command) | |
943 | { | |
944 | case RIP_CMD_REQUEST: | |
22c3cf95 OZ |
945 | case RIP_CMD_UPDATE_REQUEST: |
946 | rip_receive_request(p, ifa, pkt, pkt_len, n); | |
8465dccb OZ |
947 | break; |
948 | ||
949 | case RIP_CMD_RESPONSE: | |
22c3cf95 OZ |
950 | case RIP_CMD_UPDATE_RESPONSE: |
951 | rip_receive_response(p, ifa, pkt, pkt_len, n); | |
952 | break; | |
953 | ||
954 | case RIP_CMD_UPDATE_ACK: | |
955 | rip_receive_ack(p, ifa, pkt, pkt_len, n); | |
8465dccb OZ |
956 | break; |
957 | ||
958 | default: | |
959 | DROP("unknown command", pkt->command); | |
960 | } | |
961 | return 1; | |
962 | ||
963 | drop: | |
964 | LOG_PKT("Bad packet from %I via %s - %s (%u)", | |
965 | sk->faddr, sk->iface->name, err_dsc, err_val); | |
966 | ||
967 | return 1; | |
968 | } | |
969 | ||
970 | int | |
971 | rip_open_socket(struct rip_iface *ifa) | |
972 | { | |
973 | struct rip_proto *p = ifa->rip; | |
974 | ||
975 | sock *sk = sk_new(p->p.pool); | |
976 | sk->type = SK_UDP; | |
08b3a24d | 977 | sk->subtype = rip_is_v2(p) ? SK_IPV4 : SK_IPV6; |
8465dccb OZ |
978 | sk->sport = ifa->cf->port; |
979 | sk->dport = ifa->cf->port; | |
980 | sk->iface = ifa->iface; | |
153f02da | 981 | sk->saddr = rip_is_v2(p) ? ifa->iface->addr4->ip : ifa->iface->llv6->ip; |
943478b0 | 982 | sk->vrf = p->p.vrf; |
8465dccb OZ |
983 | |
984 | sk->rx_hook = rip_rx_hook; | |
985 | sk->tx_hook = rip_tx_hook; | |
986 | sk->err_hook = rip_err_hook; | |
987 | sk->data = ifa; | |
988 | ||
989 | sk->tos = ifa->cf->tx_tos; | |
990 | sk->priority = ifa->cf->tx_priority; | |
991 | sk->ttl = ifa->cf->ttl_security ? 255 : 1; | |
08b3a24d | 992 | sk->flags = SKF_LADDR_RX | ((ifa->cf->ttl_security == 1) ? SKF_TTL_RX : 0); |
8465dccb OZ |
993 | |
994 | /* sk->rbsize and sk->tbsize are handled in rip_iface_update_buffers() */ | |
995 | ||
996 | if (sk_open(sk) < 0) | |
997 | goto err; | |
998 | ||
999 | if (ifa->cf->mode == RIP_IM_MULTICAST) | |
1000 | { | |
1001 | if (sk_setup_multicast(sk) < 0) | |
1002 | goto err; | |
1003 | ||
1004 | if (sk_join_group(sk, ifa->addr) < 0) | |
1005 | goto err; | |
1006 | } | |
1007 | else /* Broadcast */ | |
1008 | { | |
1009 | if (sk_setup_broadcast(sk) < 0) | |
1010 | goto err; | |
1011 | ||
1012 | if (ipa_zero(ifa->addr)) | |
1013 | { | |
1014 | sk->err = "Missing broadcast address"; | |
1015 | goto err; | |
1016 | } | |
1017 | } | |
1018 | ||
1019 | ifa->sk = sk; | |
1020 | return 1; | |
1021 | ||
1022 | err: | |
1023 | sk_log_error(sk, p->p.name); | |
1024 | rfree(sk); | |
1025 | return 0; | |
1026 | } |