1 /* $OpenBSD: report.c,v 1.7 2007/04/10 09:37:25 michele Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 #include <sys/socket.h> 21 #include <netinet/in.h> 22 #include <netinet/in_systm.h> 23 #include <netinet/ip.h> 24 #include <arpa/inet.h> 25 26 #include <stdlib.h> 27 #include <strings.h> 28 29 #include "igmp.h" 30 #include "dvmrpd.h" 31 #include "dvmrp.h" 32 #include "dvmrpe.h" 33 #include "log.h" 34 35 extern struct dvmrpd_conf *deconf; 36 37 void rr_list_remove(struct route_report *); 38 39 /* DVMRP report packet handling */ 40 int 41 send_report(struct iface *iface, struct in_addr addr, void *data, int len) 42 { 43 struct sockaddr_in dst; 44 struct buf *buf; 45 struct dvmrp_hdr *dvmrp_hdr; 46 int ret = 0; 47 48 log_debug("send_report: interface %s addr %s", 49 iface->name, inet_ntoa(addr)); 50 51 if (iface->passive) 52 return (0); 53 54 if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) 55 fatal("send_report"); 56 57 /* DVMRP header */ 58 if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT)) 59 goto fail; 60 61 buf_add(buf, data, len); 62 63 dst.sin_family = AF_INET; 64 dst.sin_len = sizeof(struct sockaddr_in); 65 dst.sin_addr.s_addr = addr.s_addr; 66 67 /* update chksum */ 68 dvmrp_hdr = buf_seek(buf, 0, sizeof(dvmrp_hdr)); 69 dvmrp_hdr->chksum = in_cksum(buf->buf, buf->wpos); 70 71 ret = send_packet(iface, buf->buf, buf->wpos, &dst); 72 buf_free(buf); 73 return (ret); 74 fail: 75 log_warn("send_report"); 76 buf_free(buf); 77 return (-1); 78 } 79 80 void 81 recv_report(struct nbr *nbr, char *buf, u_int16_t len) 82 { 83 struct route_report rr; 84 u_int32_t netid, netmask; 85 u_int8_t metric, netid_len, prefixlen; 86 87 log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id)); 88 89 if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) { 90 log_warnx("recv_report: neighbor %s not in state %s", 91 inet_ntoa(nbr->id), "2-WAY"); 92 return; 93 } 94 95 /* parse route report */ 96 do { 97 /* 98 * get netmask 99 * 100 * The netmask in a DVMRP report is only represented by 3 bytes, 101 * to cope with that we read 4 bytes and shift 8 bits. 102 * The most significant part of the mask is always 255. 103 */ 104 105 /* read four bytes */ 106 memcpy(&netmask, buf, sizeof(netmask)); 107 /* ditch one byte, since we only need three */ 108 netmask = ntohl(netmask) >> 8; 109 netmask = htonl(netmask); 110 111 /* set the highest byte to 255 */ 112 netmask |= htonl(0xff000000); 113 buf += 3; 114 len -= 3; 115 116 prefixlen = mask2prefixlen(netmask); 117 netid_len = PREFIX_SIZE(prefixlen); 118 119 do { 120 /* 121 * get netid 122 * 123 * The length of the netid is depending on the above 124 * netmask. 125 * Read 4 bytes and use the netmask from above to 126 * determine the netid. 127 */ 128 memcpy(&netid, buf, sizeof(netid)); 129 netid &= netmask; 130 131 buf += netid_len; 132 len -= netid_len; 133 134 /* get metric */ 135 memcpy(&metric, buf, sizeof(metric)); 136 buf += sizeof(metric); 137 len -= sizeof(metric); 138 139 rr.net.s_addr = netid; 140 rr.mask.s_addr = netmask; 141 rr.nexthop = nbr->id; 142 rr.metric = (metric & METRIC_MASK); 143 144 /* ifindex */ 145 rr.ifindex = nbr->iface->ifindex; 146 147 /* send route report to RDE */ 148 dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid, 149 0, &rr, sizeof(rr)); 150 151 } while (!(metric & LAST_MASK) && (len > 0)); 152 } while (len > 0); 153 154 return; 155 } 156 157 /* timers */ 158 void 159 report_timer(int fd, short event, void *arg) 160 { 161 struct timeval tv; 162 163 /* request full route report */ 164 dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0); 165 166 /* restart report timer */ 167 timerclear(&tv); 168 tv.tv_sec = ROUTE_REPORT_INTERVAL; 169 evtimer_add(&deconf->report_timer, &tv); 170 } 171 172 int 173 start_report_timer(void) 174 { 175 struct timeval tv; 176 177 timerclear(&tv); 178 tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL; /* XXX safe?? */ 179 return (evtimer_add(&deconf->report_timer, &tv)); 180 } 181 182 int 183 stop_report_timer(void) 184 { 185 return (evtimer_del(&deconf->report_timer)); 186 } 187 188 /* route report list */ 189 void 190 rr_list_add(struct rr_head *rr_list, struct route_report *rr) 191 { 192 struct rr_entry *le; 193 194 if (rr == NULL) 195 fatalx("rr_list_add: no route report"); 196 197 if ((le = calloc(1, sizeof(*le))) == NULL) 198 fatal("rr_list_add"); 199 200 TAILQ_INSERT_TAIL(rr_list, le, entry); 201 le->re = rr; 202 rr->refcount++; 203 } 204 205 void 206 rr_list_remove(struct route_report *rr) 207 { 208 if (--rr->refcount == 0) 209 free(rr); 210 } 211 212 void 213 rr_list_clr(struct rr_head *rr_list) 214 { 215 struct rr_entry *le; 216 217 while ((le = TAILQ_FIRST(rr_list)) != NULL) { 218 TAILQ_REMOVE(rr_list, le, entry); 219 rr_list_remove(le->re); 220 free(le); 221 } 222 } 223 224 void 225 rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr) 226 { 227 struct rr_entry *le, *le2; 228 struct buf *buf; 229 struct iface *iface; 230 struct in_addr addr; 231 u_int32_t netid, netmask; 232 u_int8_t metric, netid_len, prefixlen; 233 234 /* set destination */ 235 if (xiface == NULL) { 236 /* directly to a nbr */ 237 iface = nbr->iface; 238 addr = nbr->addr; 239 } else { 240 /* multicast on interface */ 241 iface = xiface; 242 inet_aton(AllDVMRPRouters, &addr); 243 } 244 245 while (!TAILQ_EMPTY(rr_list)) { 246 if ((buf = buf_open(iface->mtu - sizeof(struct ip))) == NULL) 247 fatal("rr_list_send"); 248 249 prefixlen = 0; 250 while (((le = TAILQ_FIRST(rr_list)) != NULL) && 251 (buf->wpos < 1000)) { 252 /* netmask */ 253 netmask = le->re->mask.s_addr; 254 if (prefixlen != mask2prefixlen(netmask)) { 255 prefixlen = mask2prefixlen(netmask); 256 netmask = ntohl(netmask) << 8; 257 netmask = htonl(netmask); 258 buf_add(buf, &netmask, 3); 259 } 260 netid_len = PREFIX_SIZE(prefixlen); 261 262 /* netid */ 263 netid = le->re->net.s_addr; 264 buf_add(buf, &netid, netid_len); 265 266 /* metric */ 267 if (iface->ifindex == le->re->ifindex) 268 /* poison reverse */ 269 metric = le->re->metric + INFINITY_METRIC; 270 else 271 metric = le->re->metric; 272 273 /* 274 * determine if we need to flag last entry with current 275 * netmask. 276 */ 277 le2 = TAILQ_NEXT(le, entry); 278 if (le2 != NULL) { 279 if (mask2prefixlen(le2->re->mask.s_addr) != 280 prefixlen) 281 metric = metric | LAST_MASK; 282 } else { 283 metric = metric | LAST_MASK; 284 } 285 286 buf_add(buf, &metric, sizeof(metric)); 287 288 TAILQ_REMOVE(rr_list, le, entry); 289 rr_list_remove(le->re); 290 free(le); 291 } 292 send_report(iface, addr, buf->buf, buf->wpos); 293 buf_free(buf); 294 } 295 } 296