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