/* $OpenBSD: report.c,v 1.5 2006/11/10 11:15:32 michele Exp $ */ /* * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <stdlib.h> #include <strings.h> #include "igmp.h" #include "dvmrpd.h" #include "dvmrp.h" #include "dvmrpe.h" #include "log.h" extern struct dvmrpd_conf *deconf; void rr_list_remove(struct route_report *); /* DVMRP report packet handling */ int send_report(struct iface *iface, struct in_addr addr, void *data, int len) { struct sockaddr_in dst; struct buf *buf; struct dvmrp_hdr *dvmrp_hdr; int ret = 0; log_debug("send_report: interface %s addr %s", iface->name, inet_ntoa(addr)); if (iface->passive) return (0); if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) fatal("send_report"); /* DVMRP header */ if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT)) goto fail; buf_add(buf, data, len); dst.sin_family = AF_INET; dst.sin_len = sizeof(struct sockaddr_in); dst.sin_addr.s_addr = addr.s_addr; /* update chksum */ dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); ret = send_packet(iface, buf->buf, buf->wpos, &dst); buf_free(buf); return (ret); fail: log_warn("send_report"); buf_free(buf); return (-1); } void recv_report(struct nbr *nbr, char *buf, u_int16_t len) { struct route_report rr; u_int32_t netid, netmask; u_int8_t metric, netid_len, prefixlen; log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id)); if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) { log_warnx("recv_report: neighbor %s not in state %s", inet_ntoa(nbr->id), "2-WAY"); return; } /* parse route report */ do { /* * get netmask * * The netmask in a DVMRP report is only represented by 3 bytes, * to cope with that we read 4 bytes and shift 8 bits. * The most significant part of the mask is always 255. */ /* read four bytes */ memcpy(&netmask, buf, sizeof(netmask)); /* ditch one byte, since we only need three */ netmask = ntohl(netmask) >> 8; netmask = htonl(netmask); /* set the highest byte to 255 */ netmask |= htonl(0xff000000); buf += 3; len -= 3; prefixlen = mask2prefixlen(netmask); netid_len = PREFIX_SIZE(prefixlen); do { /* * get netid * * The length of the netid is depending on the above * netmask. * Read 4 bytes and use the netmask from above to * determine the netid. */ memcpy(&netid, buf, sizeof(netid)); netid &= netmask; /* Interpret special case 0.0.0.0/8 as 0.0.0.0/0 */ if (netid == 0) netmask = 0; buf += netid_len; len -= netid_len; /* get metric */ memcpy(&metric, buf, sizeof(metric)); buf += sizeof(metric); len -= sizeof(metric); rr.net.s_addr = netid; rr.mask.s_addr = netmask; rr.nexthop = nbr->id; /* adjusted metric */ rr.metric = (metric & METRIC_MASK) + nbr->iface->metric; /* ifindex */ rr.ifindex = nbr->iface->ifindex; /* send route report to RDE */ dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid, 0, &rr, sizeof(rr)); } while (!(metric & LAST_MASK) && (len > 0)); } while (len > 0); return; } /* timers */ void report_timer(int fd, short event, void *arg) { struct timeval tv; /* request full route report */ dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0); /* restart report timer */ timerclear(&tv); tv.tv_sec = ROUTE_REPORT_INTERVAL; evtimer_add(&deconf->report_timer, &tv); } int start_report_timer(void) { struct timeval tv; timerclear(&tv); tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL; /* XXX safe?? */ return (evtimer_add(&deconf->report_timer, &tv)); } int stop_report_timer(void) { return (evtimer_del(&deconf->report_timer)); } /* route report list */ void rr_list_add(struct rr_head *rr_list, struct route_report *rr) { struct rr_entry *le; if (rr == NULL) fatalx("rr_list_add: no route report"); if ((le = calloc(1, sizeof(*le))) == NULL) fatal("rr_list_add"); TAILQ_INSERT_TAIL(rr_list, le, entry); le->re = rr; rr->refcount++; } void rr_list_remove(struct route_report *rr) { if (--rr->refcount == 0) free(rr); } void rr_list_clr(struct rr_head *rr_list) { struct rr_entry *le; while ((le = TAILQ_FIRST(rr_list)) != NULL) { TAILQ_REMOVE(rr_list, le, entry); rr_list_remove(le->re); free(le); } } void rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr) { struct rr_entry *le, *le2; struct buf *buf; struct iface *iface; struct in_addr addr; u_int32_t netid, netmask; u_int8_t metric, netid_len, prefixlen; /* set destination */ if (xiface == NULL) { /* directly to a nbr */ iface = nbr->iface; addr = nbr->addr; } else { /* multicast on interface */ iface = xiface; inet_aton(AllDVMRPRouters, &addr); } while (!TAILQ_EMPTY(rr_list)) { if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) fatal("rr_list_send"); prefixlen = 0; while (((le = TAILQ_FIRST(rr_list)) != NULL) && (buf->wpos < 1000)) { /* netmask */ netmask = le->re->mask.s_addr; if (prefixlen != mask2prefixlen(netmask)) { prefixlen = mask2prefixlen(netmask); netmask = ntohl(netmask) << 8; netmask = htonl(netmask); buf_add(buf, &netmask, 3); } netid_len = PREFIX_SIZE(prefixlen); /* netid */ netid = le->re->net.s_addr; buf_add(buf, &netid, netid_len); /* metric */ if (iface->ifindex == le->re->ifindex) /* poison reverse */ metric = le->re->metric + INFINITY_METRIC; else metric = le->re->metric; /* * determine if we need to flag last entry with current * netmask. */ le2 = TAILQ_NEXT(le, entry); if (le2 != NULL) { if (mask2prefixlen(le2->re->mask.s_addr) != prefixlen) metric = metric | LAST_MASK; } else { metric = metric | LAST_MASK; } buf_add(buf, &metric, sizeof(metric)); TAILQ_REMOVE(rr_list, le, entry); rr_list_remove(le->re); free(le); } send_report(iface, addr, buf->buf, buf->wpos); buf_free(buf); } }