Annotation of embedaddon/pimd/pim.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (c) 1998-2001
3: * University of Southern California/Information Sciences Institute.
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: * 1. Redistributions of source code must retain the above copyright
10: * notice, this list of conditions and the following disclaimer.
11: * 2. Redistributions in binary form must reproduce the above copyright
12: * notice, this list of conditions and the following disclaimer in the
13: * documentation and/or other materials provided with the distribution.
14: * 3. Neither the name of the project nor the names of its contributors
15: * may be used to endorse or promote products derived from this software
16: * without specific prior written permission.
17: *
18: * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
19: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21: * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
22: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28: * SUCH DAMAGE.
29: */
30: /*
31: * $Id: pim.c,v 1.24 2002/09/26 00:59:30 pavlin Exp $
32: */
33:
34: #include "defs.h"
35:
36: #define SEND_DEBUG_NUMBER 50 /* For throttling log messages */
37:
38: /*
39: * Exported variables.
40: */
41: char *pim_recv_buf; /* input packet buffer */
42: char *pim_send_buf; /* output packet buffer */
43:
44: uint32_t allpimrouters_group; /* ALL_PIM_ROUTERS address in net order */
45: int pim_socket; /* socket for PIM control msgs */
46:
47: #ifdef RAW_OUTPUT_IS_RAW
48: extern int curttl;
49: #endif /* RAW_OUTPUT_IS_RAW */
50:
51: /*
52: * Local variables.
53: */
54: static uint16_t ip_id = 0;
55: //static u_int pim_send_cnt = 0;
56:
57:
58: /*
59: * Local function definitions.
60: */
61: static void pim_read (int f, fd_set *rfd);
62: static void accept_pim (ssize_t recvlen);
63: static int send_frame (char *buf, size_t len, size_t frag, size_t mtu, struct sockaddr *dst, size_t salen);
64:
65: /*
66: * Setup raw kernel socket for PIM protocol and send/receive buffers.
67: */
68: void init_pim(void)
69: {
70: struct ip *ip;
71:
72: /* Setup the PIM raw socket */
73: if ((pim_socket = socket(AF_INET, SOCK_RAW, IPPROTO_PIM)) < 0)
74: logit(LOG_ERR, errno, "Failed creating PIM socket");
75: k_hdr_include(pim_socket, TRUE); /* include IP header when sending */
76: k_set_sndbuf(pim_socket, SO_SEND_BUF_SIZE_MAX,
77: SO_SEND_BUF_SIZE_MIN); /* lots of output buffering */
78: k_set_rcvbuf(pim_socket, SO_RECV_BUF_SIZE_MAX,
79: SO_RECV_BUF_SIZE_MIN); /* lots of input buffering */
80: k_set_ttl(pim_socket, MINTTL); /* restrict multicasts to one hop */
81: k_set_loop(pim_socket, FALSE); /* disable multicast loopback */
82:
83: allpimrouters_group = htonl(INADDR_ALL_PIM_ROUTERS);
84:
85: pim_recv_buf = calloc(1, RECV_BUF_SIZE);
86: pim_send_buf = calloc(1, SEND_BUF_SIZE);
87: if (!pim_recv_buf || !pim_send_buf)
88: logit(LOG_ERR, 0, "Ran out of memory in init_pim()");
89:
90: /* One time setup in the buffers */
91: ip = (struct ip *)pim_send_buf;
92: memset(ip, 0, sizeof(*ip));
93: ip->ip_v = IPVERSION;
94: ip->ip_hl = (sizeof(struct ip) >> 2);
95: ip->ip_tos = 0; /* TODO: setup?? */
96: ip->ip_id = 0; /* Make sure to update ID field, maybe fragmenting below */
97: ip->ip_off = 0;
98: ip->ip_p = IPPROTO_PIM;
99: ip->ip_sum = 0; /* let kernel fill in */
100:
101: if (register_input_handler(pim_socket, pim_read) < 0)
102: logit(LOG_ERR, 0, "Failed registering pim_read() as an input handler");
103:
104: /* Initialize the building Join/Prune messages working area */
105: build_jp_message_pool = (build_jp_message_t *)NULL;
106: build_jp_message_pool_counter = 0;
107: }
108:
109:
110: /* Read a PIM message */
111: static void pim_read(int f __attribute__((unused)), fd_set *rfd __attribute__((unused)))
112: {
113: ssize_t len;
114: socklen_t dummy = 0;
115: sigset_t block, oblock;
116:
117: while ((len = recvfrom(pim_socket, pim_recv_buf, RECV_BUF_SIZE, 0, NULL, &dummy)) < 0) {
118: if (errno == EINTR)
119: continue; /* Received signal, retry syscall. */
120:
121: logit(LOG_ERR, errno, "Failed recvfrom() in pim_read()");
122: return;
123: }
124:
125: sigemptyset(&block);
126: sigaddset(&block, SIGALRM);
127: if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
128: logit(LOG_ERR, errno, "sigprocmask");
129:
130: accept_pim(len);
131:
132: sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
133: }
134:
135: static void accept_pim(ssize_t recvlen)
136: {
137: uint32_t src, dst;
138: struct ip *ip;
139: pim_header_t *pim;
140: int iphdrlen, pimlen;
141: char source[20], dest[20];
142:
143: if (recvlen < (ssize_t)sizeof(struct ip)) {
144: logit(LOG_WARNING, 0, "Received PIM packet too short (%u bytes) for IP header", recvlen);
145: return;
146: }
147:
148: ip = (struct ip *)pim_recv_buf;
149: src = ip->ip_src.s_addr;
150: dst = ip->ip_dst.s_addr;
151: iphdrlen = ip->ip_hl << 2;
152:
153: pim = (pim_header_t *)(pim_recv_buf + iphdrlen);
154: pimlen = recvlen - iphdrlen;
155:
156: /* Sanity check packet length */
157: if (pimlen < (ssize_t)sizeof(*pim)) {
158: logit(LOG_WARNING, 0, "IP data field too short (%d bytes) for PIM header, from %s to %s",
159: pimlen, inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
160: return;
161: }
162:
163: IF_DEBUG(DEBUG_PIM_DETAIL) {
164: IF_DEBUG(DEBUG_PIM) {
165: logit(LOG_DEBUG, 0, "RECV %5d bytes %s from %-15s to %s ", recvlen,
166: packet_kind(IPPROTO_PIM, pim->pim_type, 0),
167: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
168: }
169: }
170:
171: /* TODO: Check PIM version */
172: /* TODO: check the dest. is ALL_PIM_ROUTERS (if multicast address) */
173: /* TODO: Checksum verification is done in each of the processing functions.
174: * No need for checksum, if already done in the kernel?
175: */
176: switch (pim->pim_type) {
177: case PIM_HELLO:
178: receive_pim_hello(src, dst, (char *)(pim), pimlen);
179: break;
180:
181: case PIM_REGISTER:
182: receive_pim_register(src, dst, (char *)(pim), pimlen);
183: break;
184:
185: case PIM_REGISTER_STOP:
186: receive_pim_register_stop(src, dst, (char *)(pim), pimlen);
187: break;
188:
189: case PIM_JOIN_PRUNE:
190: receive_pim_join_prune(src, dst, (char *)(pim), pimlen);
191: break;
192:
193: case PIM_BOOTSTRAP:
194: receive_pim_bootstrap(src, dst, (char *)(pim), pimlen);
195: break;
196:
197: case PIM_ASSERT:
198: receive_pim_assert(src, dst, (char *)(pim), pimlen);
199: break;
200:
201: case PIM_GRAFT:
202: case PIM_GRAFT_ACK:
203: logit(LOG_INFO, 0, "ignore %s from %s to %s", packet_kind(IPPROTO_PIM, pim->pim_type, 0),
204: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
205: break;
206:
207: case PIM_CAND_RP_ADV:
208: receive_pim_cand_rp_adv(src, dst, (char *)(pim), pimlen);
209: break;
210:
211: default:
212: logit(LOG_INFO, 0, "ignore unknown PIM message code %u from %s to %s", pim->pim_type,
213: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
214: break;
215: }
216: }
217:
218:
219: /*
220: * Send a multicast PIM packet from src to dst, PIM message type = "type"
221: * and data length (after the PIM header) = "len"
222: */
223: void send_pim(char *buf, uint32_t src, uint32_t dst, int type, size_t len)
224: {
225: struct sockaddr_in sin;
226: struct ip *ip;
227: pim_header_t *pim;
228: int sendlen = sizeof(struct ip) + sizeof(pim_header_t) + len;
229: int setloop = 0;
230: char source[20], dest[20];
231:
232: /* Prepare the IP header */
233: ip = (struct ip *)buf;
234: ip->ip_id = htons(++ip_id);
235: ip->ip_off = 0;
236: ip->ip_src.s_addr = src;
237: ip->ip_dst.s_addr = dst;
238: ip->ip_ttl = MAXTTL; /* applies to unicast only */
239: #ifdef HAVE_IP_HDRINCL_BSD_ORDER
240: ip->ip_len = sendlen;
241: #else
242: ip->ip_len = htons(sendlen);
243: #endif
244:
245: /* Prepare the PIM packet */
246: pim = (pim_header_t *)(buf + sizeof(struct ip));
247: pim->pim_type = type;
248: pim->pim_vers = PIM_PROTOCOL_VERSION;
249: pim->pim_reserved = 0;
250: pim->pim_cksum = 0;
251:
252: /* TODO: XXX: if start using this code for PIM_REGISTERS, exclude the
253: * encapsulated packet from the checsum. */
254: pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + len);
255:
256: if (IN_MULTICAST(ntohl(dst))) {
257: k_set_if(pim_socket, src);
258: if ((dst == allhosts_group) ||
259: (dst == allrouters_group) ||
260: (dst == allpimrouters_group) ||
261: (dst == allreports_group)) {
262: setloop = 1;
263: k_set_loop(pim_socket, TRUE);
264: }
265: #ifdef RAW_OUTPUT_IS_RAW
266: ip->ip_ttl = curttl;
267: } else {
268: ip->ip_ttl = MAXTTL;
269: #endif /* RAW_OUTPUT_IS_RAW */
270: }
271:
272: memset(&sin, 0, sizeof(sin));
273: sin.sin_family = AF_INET;
274: sin.sin_addr.s_addr = dst;
275: #ifdef HAVE_SA_LEN
276: sin.sin_len = sizeof(sin);
277: #endif
278:
279: while (sendto(pim_socket, buf, sendlen, 0, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
280: if (errno == EINTR)
281: continue; /* Received signal, retry syscall. */
282: if (errno == ENETDOWN || errno == ENODEV)
283: check_vif_state();
284: else if (errno == EPERM || errno == EHOSTUNREACH)
285: logit(LOG_WARNING, 0, "Not allowed to send PIM message from %s to %s, possibly firewall"
286: #ifdef __linux__
287: ", or SELinux policy violation,"
288: #endif
289: " related problem."
290: ,
291: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
292: else
293: logit(LOG_WARNING, errno, "sendto from %s to %s",
294: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
295: if (setloop)
296: k_set_loop(pim_socket, FALSE);
297: return;
298: }
299:
300: if (setloop)
301: k_set_loop(pim_socket, FALSE);
302:
303: IF_DEBUG(DEBUG_PIM_DETAIL) {
304: IF_DEBUG(DEBUG_PIM) {
305: logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s",
306: sendlen, packet_kind(IPPROTO_PIM, type, 0),
307: src == INADDR_ANY_N ? "INADDR_ANY" :
308: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
309: }
310: }
311: }
312:
313:
314: /* TODO: This can be merged with the above procedure */
315: /*
316: * Send an unicast PIM packet from src to dst, PIM message type = "type"
317: * and data length (after the PIM common header) = "len"
318: */
319: void send_pim_unicast(char *buf, int mtu, uint32_t src, uint32_t dst, int type, size_t len)
320: {
321: struct sockaddr_in sin;
322: struct ip *ip;
323: pim_header_t *pim;
324: int result, sendlen = sizeof(struct ip) + sizeof(pim_header_t) + len;
325: char source[20], dest[20];
326:
327: /* Prepare the IP header */
328: ip = (struct ip *)buf;
329: ip->ip_id = htons(++ip_id);
330: ip->ip_src.s_addr = src;
331: ip->ip_dst.s_addr = dst;
332: ip->ip_ttl = MAXTTL; /* TODO: XXX: setup TTL from the inner mcast packet? */
333: #ifdef HAVE_IP_HDRINCL_BSD_ORDER
334: ip->ip_len = sendlen;
335: #else
336: ip->ip_len = htons(sendlen);
337: #endif
338:
339: /* Prepare the PIM packet */
340: pim = (pim_header_t *)(buf + sizeof(struct ip));
341: pim->pim_type = type;
342: pim->pim_vers = PIM_PROTOCOL_VERSION;
343: pim->pim_reserved = 0;
344: pim->pim_cksum = 0;
345:
346: /* XXX: The PIM_REGISTERs don't include the encapsulated
347: * inner packet in the checksum.
348: * Well, try to explain this to cisco...
349: * If your RP is cisco and if it shows many PIM_REGISTER checksum
350: * errors from this router, then #define BROKEN_CISCO_CHECKSUM here
351: * or in your Makefile.
352: * Note that such checksum is not in the spec, and such PIM_REGISTERS
353: * may be dropped by some implementations (pimd should be OK).
354: */
355: #ifdef BROKEN_CISCO_CHECKSUM
356: pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + len);
357: #else
358: if (PIM_REGISTER == type) {
359: pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + sizeof(pim_register_t));
360: } else {
361: pim->pim_cksum = inet_cksum((uint16_t *)pim, sizeof(pim_header_t) + len);
362: }
363: #endif /* BROKEN_CISCO_CHECKSUM */
364:
365: memset(&sin, 0, sizeof(sin));
366: sin.sin_family = AF_INET;
367: sin.sin_addr.s_addr = dst;
368: #ifdef HAVE_SA_LEN
369: sin.sin_len = sizeof(sin);
370: #endif
371:
372: IF_DEBUG(DEBUG_PIM_DETAIL) {
373: IF_DEBUG(DEBUG_PIM) {
374: logit(LOG_DEBUG, 0, "SEND %5d bytes %s from %-15s to %s ...",
375: sendlen, packet_kind(IPPROTO_PIM, type, 0),
376: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
377: }
378: }
379:
380: result = send_frame(buf, sendlen, 0, mtu, (struct sockaddr *)&sin, sizeof(sin));
381: if (result) {
382: logit(LOG_WARNING, errno, "sendto from %s to %s",
383: inet_fmt(ip->ip_src.s_addr, source, sizeof(source)),
384: inet_fmt(ip->ip_dst.s_addr, dest, sizeof(dest)));
385: return;
386: }
387:
388: IF_DEBUG(DEBUG_PIM_DETAIL) {
389: IF_DEBUG(DEBUG_PIM) {
390: #if 0 /* TODO: use pim_send_cnt? */
391: if (++pim_send_cnt > SEND_DEBUG_NUMBER) {
392: pim_send_cnt = 0;
393: logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s",
394: sendlen, packet_kind(IPPROTO_PIM, type, 0),
395: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
396: }
397: #endif
398: logit(LOG_DEBUG, 0, "SENT %5d bytes %s from %-15s to %s",
399: sendlen, packet_kind(IPPROTO_PIM, type, 0),
400: inet_fmt(src, source, sizeof(source)), inet_fmt(dst, dest, sizeof(dest)));
401: }
402: }
403: }
404:
405:
406: #if 1
407: /*
408: * send unicast register frames
409: * Version: Michael Fine
410: * Staus: Works, albeit non-optimal
411: * Design: Only fragments if sendto() fails with EMSGSIZE
412: * It then tries to re-send by splitting the frame in two equal halves,
413: * calling send_frame() recursively until the frame has been sent.
414: */
415: static int send_frame(char *buf, size_t len, size_t frag, size_t mtu, struct sockaddr *dst, size_t salen)
416: {
417: struct ip *ip = (struct ip *)buf;
418: char source[20], dest[20];
419:
420: IF_DEBUG(DEBUG_PIM_REGISTER) {
421: logit(LOG_INFO, 0, "Sending unicast: len = %d, frag %zd, mtu %zd, to %s",
422: len, frag, mtu, inet_fmt(ip->ip_dst.s_addr, source, sizeof(source)));
423: dump_frame(NULL, buf, len);
424: }
425:
426: while (sendto(pim_socket, buf, len, 0, dst, salen) < 0) {
427: switch (errno) {
428: case EINTR:
429: continue; /* Received signal, retry syscall. */
430:
431: case ENETDOWN:
432: check_vif_state();
433: return -1;
434:
435: case EMSGSIZE:
436: {
437: /* split it in half and recursively send each half */
438: size_t hdrsize = sizeof(struct ip);
439: size_t newlen1 = ((len - hdrsize) / 2) & 0xFFF8; /* 8 byte boundary */
440: size_t sendlen = newlen1 + hdrsize;
441: size_t offset = ntohs(ip->ip_off);
442:
443: /* send first half */
444: ip->ip_len = htons(sendlen);
445: ip->ip_off = htons(offset | IP_MF);
446: if (send_frame(buf, sendlen, 1, newlen1, dst, salen) == 0) {
447: /* send second half */
448: struct ip *ip2 = (struct ip *)(buf + newlen1);
449: size_t newlen2 = len - sendlen;
450: sendlen = newlen2 + hdrsize;
451:
452: memcpy(ip2, ip, hdrsize);
453: ip2->ip_len = htons(sendlen);
454: ip2->ip_off = htons(offset + (newlen1 >> 3)); /* keep flgs */
455: return send_frame((char *)ip2, sendlen, 1, newlen2, dst, salen);
456: }
457:
458: return -1;
459: }
460:
461: default:
462: logit(LOG_WARNING, errno, "sendto from %s to %s",
463: inet_fmt(ip->ip_src.s_addr, source, sizeof(source)),
464: inet_fmt(ip->ip_dst.s_addr, dest, sizeof(dest)));
465: return -1;
466: }
467: }
468:
469: return 0;
470: }
471:
472: #else
473: /*
474: * send unicast register frames
475: * Version: Joachim Nilsson
476: * Staus: Does not work (yet!)
477: * Design: Fragment IP frames when the frame length exceeds the MTU
478: * reported from the interface. Optimizes for less fragments
479: * and fewer syscalls, should get better network utilization.
480: * Can be easily modified to use the PMTU instead.
481: *
482: * Feel free to debug this version and submit your patches -- it should work! --Joachim
483: */
484: static int send_frame(char *buf, size_t len, size_t frag, size_t mtu, struct sockaddr *dst, size_t salen)
485: {
486: struct ip *next, *ip = (struct ip *)buf;
487: size_t xferlen, offset;
488: char source[20], dest[20];
489:
490: if (!mtu)
491: mtu = IP_MSS;
492:
493: if (len > mtu)
494: xferlen = (mtu - sizeof(struct ip)) & 0xFFF8;
495: else
496: xferlen = len;
497:
498: offset = (ntohs(ip->ip_off) & IP_OFFMASK) + (frag >> 3);
499: ip->ip_off = offset;
500: len = len - xferlen;
501: if (len)
502: ip->ip_off |= IP_MF;
503: ip->ip_off = htons(ip->ip_off);
504: ip->ip_len = htons(xferlen);
505:
506: IF_DEBUG(DEBUG_PIM_REGISTER) {
507: logit(LOG_INFO, 0, "Sending %-4d bytes %sunicast (MTU %-4d, offset %zd) to %s",
508: xferlen, len ? "fragmented " : "", mtu, offset,
509: inet_fmt(ip->ip_dst.s_addr, source, sizeof(source)));
510: dump_frame(NULL, buf, xferlen);
511: }
512:
513: /* send first fragment */
514: while (sendto(pim_socket, ip, xferlen, 0, dst, salen) < 0) {
515: switch (errno) {
516: case EINTR:
517: continue; /* Received signal, retry syscall. */
518:
519: case ENETDOWN:
520: check_vif_state();
521: return -1;
522:
523: case EMSGSIZE:
524: if (mtu > IP_MSS)
525: return send_frame((char *)ip, xferlen, frag, IP_MSS, dst, salen);
526: /* fall through */
527:
528: default:
529: return -1;
530: }
531: }
532:
533: /* send reminder */
534: if (len) {
535: size_t hdrsz = sizeof(struct ip);
536:
537: /* Update data pointers */
538: next = (struct ip *)(buf + xferlen - hdrsz);
539: memcpy(next, ip, hdrsz);
540:
541: return send_frame((char *)next, len + hdrsz, xferlen, mtu, dst, salen);
542: }
543:
544: return 0;
545: }
546: #endif
547:
548: /**
549: * Local Variables:
550: * version-control: t
551: * indent-tabs-mode: t
552: * c-file-style: "ellemtel"
553: * c-basic-offset: 4
554: * End:
555: */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>