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