1*2471dd62Sflorian /* $OpenBSD: report.c,v 1.13 2024/08/21 09:18:47 florian Exp $ */ 2978e5cffSnorby 3978e5cffSnorby /* 4978e5cffSnorby * Copyright (c) 2005, 2006 Esben Norby <norby@openbsd.org> 5978e5cffSnorby * 6978e5cffSnorby * Permission to use, copy, modify, and distribute this software for any 7978e5cffSnorby * purpose with or without fee is hereby granted, provided that the above 8978e5cffSnorby * copyright notice and this permission notice appear in all copies. 9978e5cffSnorby * 10978e5cffSnorby * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11978e5cffSnorby * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12978e5cffSnorby * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13978e5cffSnorby * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14978e5cffSnorby * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15978e5cffSnorby * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16978e5cffSnorby * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17978e5cffSnorby */ 18978e5cffSnorby 19978e5cffSnorby #include <sys/types.h> 20978e5cffSnorby #include <sys/socket.h> 21978e5cffSnorby #include <netinet/in.h> 22978e5cffSnorby #include <netinet/ip.h> 23978e5cffSnorby #include <arpa/inet.h> 24978e5cffSnorby 25978e5cffSnorby #include <stdlib.h> 26b53a5054Smmcc #include <string.h> 27978e5cffSnorby 28978e5cffSnorby #include "igmp.h" 29978e5cffSnorby #include "dvmrpd.h" 30978e5cffSnorby #include "dvmrp.h" 31978e5cffSnorby #include "dvmrpe.h" 32978e5cffSnorby #include "log.h" 33978e5cffSnorby 34978e5cffSnorby extern struct dvmrpd_conf *deconf; 35978e5cffSnorby 3650b67c3fSmichele void rr_list_remove(struct route_report *); 37978e5cffSnorby 38978e5cffSnorby /* DVMRP report packet handling */ 39978e5cffSnorby int 40978e5cffSnorby send_report(struct iface *iface, struct in_addr addr, void *data, int len) 41978e5cffSnorby { 42978e5cffSnorby struct sockaddr_in dst; 43e39620e5Snicm struct ibuf *buf; 44978e5cffSnorby int ret = 0; 45978e5cffSnorby 46978e5cffSnorby log_debug("send_report: interface %s addr %s", 47978e5cffSnorby iface->name, inet_ntoa(addr)); 48978e5cffSnorby 49978e5cffSnorby if (iface->passive) 50978e5cffSnorby return (0); 51978e5cffSnorby 52e39620e5Snicm if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL) 53978e5cffSnorby fatal("send_report"); 54978e5cffSnorby 55978e5cffSnorby /* DVMRP header */ 56978e5cffSnorby if (gen_dvmrp_hdr(buf, iface, DVMRP_CODE_REPORT)) 57978e5cffSnorby goto fail; 58978e5cffSnorby 59e39620e5Snicm ibuf_add(buf, data, len); 60978e5cffSnorby 61978e5cffSnorby dst.sin_family = AF_INET; 62978e5cffSnorby dst.sin_len = sizeof(struct sockaddr_in); 63978e5cffSnorby dst.sin_addr.s_addr = addr.s_addr; 64978e5cffSnorby 6535f7e862Sclaudio ret = send_packet(iface, buf, &dst); 66e39620e5Snicm ibuf_free(buf); 67978e5cffSnorby return (ret); 68978e5cffSnorby fail: 69978e5cffSnorby log_warn("send_report"); 70e39620e5Snicm ibuf_free(buf); 71978e5cffSnorby return (-1); 72978e5cffSnorby } 73978e5cffSnorby 74978e5cffSnorby void 75978e5cffSnorby recv_report(struct nbr *nbr, char *buf, u_int16_t len) 76978e5cffSnorby { 77978e5cffSnorby struct route_report rr; 78978e5cffSnorby u_int32_t netid, netmask; 79978e5cffSnorby u_int8_t metric, netid_len, prefixlen; 80978e5cffSnorby 81978e5cffSnorby log_debug("recv_report: neighbor ID %s", inet_ntoa(nbr->id)); 82978e5cffSnorby 83978e5cffSnorby if ((nbr->state != NBR_STA_2_WAY) && (!nbr->compat)) { 84978e5cffSnorby log_warnx("recv_report: neighbor %s not in state %s", 85978e5cffSnorby inet_ntoa(nbr->id), "2-WAY"); 86978e5cffSnorby return; 87978e5cffSnorby } 88978e5cffSnorby 89978e5cffSnorby /* parse route report */ 90978e5cffSnorby do { 91978e5cffSnorby /* 92978e5cffSnorby * get netmask 93978e5cffSnorby * 94978e5cffSnorby * The netmask in a DVMRP report is only represented by 3 bytes, 95978e5cffSnorby * to cope with that we read 4 bytes and shift 8 bits. 96978e5cffSnorby * The most significant part of the mask is always 255. 97978e5cffSnorby */ 98978e5cffSnorby 99978e5cffSnorby /* read four bytes */ 100978e5cffSnorby memcpy(&netmask, buf, sizeof(netmask)); 101978e5cffSnorby /* ditch one byte, since we only need three */ 102acfb6403Snorby netmask = ntohl(netmask) >> 8; 103acfb6403Snorby netmask = htonl(netmask); 104acfb6403Snorby 105978e5cffSnorby /* set the highest byte to 255 */ 1068b293cb2Smichele netmask |= htonl(0xff000000); 107978e5cffSnorby buf += 3; 108978e5cffSnorby len -= 3; 109978e5cffSnorby 110978e5cffSnorby prefixlen = mask2prefixlen(netmask); 11113d89169Snorby netid_len = PREFIX_SIZE(prefixlen); 112978e5cffSnorby 113978e5cffSnorby do { 114978e5cffSnorby /* 115978e5cffSnorby * get netid 116978e5cffSnorby * 117978e5cffSnorby * The length of the netid is depending on the above 118978e5cffSnorby * netmask. 119978e5cffSnorby * Read 4 bytes and use the netmask from above to 120978e5cffSnorby * determine the netid. 121978e5cffSnorby */ 122978e5cffSnorby memcpy(&netid, buf, sizeof(netid)); 1238b293cb2Smichele netid &= netmask; 1248b293cb2Smichele 125978e5cffSnorby buf += netid_len; 126978e5cffSnorby len -= netid_len; 127978e5cffSnorby 128978e5cffSnorby /* get metric */ 129978e5cffSnorby memcpy(&metric, buf, sizeof(metric)); 130978e5cffSnorby buf += sizeof(metric); 131978e5cffSnorby len -= sizeof(metric); 132978e5cffSnorby 133978e5cffSnorby rr.net.s_addr = netid; 134978e5cffSnorby rr.mask.s_addr = netmask; 135978e5cffSnorby rr.nexthop = nbr->id; 136a2ed222dSmichele rr.metric = (metric & METRIC_MASK); 137978e5cffSnorby 138978e5cffSnorby /* ifindex */ 139978e5cffSnorby rr.ifindex = nbr->iface->ifindex; 140978e5cffSnorby 141978e5cffSnorby /* send route report to RDE */ 142978e5cffSnorby dvmrpe_imsg_compose_rde(IMSG_ROUTE_REPORT, nbr->peerid, 143978e5cffSnorby 0, &rr, sizeof(rr)); 144978e5cffSnorby 145978e5cffSnorby } while (!(metric & LAST_MASK) && (len > 0)); 146978e5cffSnorby } while (len > 0); 147978e5cffSnorby 148978e5cffSnorby return; 149978e5cffSnorby } 150978e5cffSnorby 151978e5cffSnorby /* timers */ 152978e5cffSnorby void 153978e5cffSnorby report_timer(int fd, short event, void *arg) 154978e5cffSnorby { 155978e5cffSnorby struct timeval tv; 156978e5cffSnorby 157978e5cffSnorby /* request full route report */ 158978e5cffSnorby dvmrpe_imsg_compose_rde(IMSG_FULL_ROUTE_REPORT, 0, 0, NULL, 0); 159978e5cffSnorby 160978e5cffSnorby /* restart report timer */ 161978e5cffSnorby timerclear(&tv); 162978e5cffSnorby tv.tv_sec = ROUTE_REPORT_INTERVAL; 163978e5cffSnorby evtimer_add(&deconf->report_timer, &tv); 164978e5cffSnorby } 165978e5cffSnorby 166978e5cffSnorby int 167978e5cffSnorby start_report_timer(void) 168978e5cffSnorby { 169978e5cffSnorby struct timeval tv; 170978e5cffSnorby 171978e5cffSnorby timerclear(&tv); 172978e5cffSnorby tv.tv_sec = MIN_FLASH_UPDATE_INTERVAL; /* XXX safe?? */ 173978e5cffSnorby return (evtimer_add(&deconf->report_timer, &tv)); 174978e5cffSnorby } 175978e5cffSnorby 176978e5cffSnorby int 177978e5cffSnorby stop_report_timer(void) 178978e5cffSnorby { 179978e5cffSnorby return (evtimer_del(&deconf->report_timer)); 180978e5cffSnorby } 181978e5cffSnorby 182978e5cffSnorby /* route report list */ 183978e5cffSnorby void 184978e5cffSnorby rr_list_add(struct rr_head *rr_list, struct route_report *rr) 185978e5cffSnorby { 186978e5cffSnorby struct rr_entry *le; 187978e5cffSnorby 188978e5cffSnorby if (rr == NULL) 189978e5cffSnorby fatalx("rr_list_add: no route report"); 190978e5cffSnorby 191978e5cffSnorby if ((le = calloc(1, sizeof(*le))) == NULL) 192978e5cffSnorby fatal("rr_list_add"); 193978e5cffSnorby 194978e5cffSnorby TAILQ_INSERT_TAIL(rr_list, le, entry); 195978e5cffSnorby le->re = rr; 19650b67c3fSmichele rr->refcount++; 19750b67c3fSmichele } 19850b67c3fSmichele 19950b67c3fSmichele void 20050b67c3fSmichele rr_list_remove(struct route_report *rr) 20150b67c3fSmichele { 20250b67c3fSmichele if (--rr->refcount == 0) 20350b67c3fSmichele free(rr); 204978e5cffSnorby } 205978e5cffSnorby 206978e5cffSnorby void 207978e5cffSnorby rr_list_clr(struct rr_head *rr_list) 208978e5cffSnorby { 209978e5cffSnorby struct rr_entry *le; 210978e5cffSnorby 211978e5cffSnorby while ((le = TAILQ_FIRST(rr_list)) != NULL) { 212978e5cffSnorby TAILQ_REMOVE(rr_list, le, entry); 21350b67c3fSmichele rr_list_remove(le->re); 214978e5cffSnorby free(le); 215978e5cffSnorby } 216978e5cffSnorby } 217978e5cffSnorby 218978e5cffSnorby void 219978e5cffSnorby rr_list_send(struct rr_head *rr_list, struct iface *xiface, struct nbr *nbr) 220978e5cffSnorby { 221978e5cffSnorby struct rr_entry *le, *le2; 222e39620e5Snicm struct ibuf *buf; 223978e5cffSnorby struct iface *iface; 224978e5cffSnorby struct in_addr addr; 225978e5cffSnorby u_int32_t netid, netmask; 226978e5cffSnorby u_int8_t metric, netid_len, prefixlen; 227978e5cffSnorby 228978e5cffSnorby /* set destination */ 229978e5cffSnorby if (xiface == NULL) { 230978e5cffSnorby /* directly to a nbr */ 231978e5cffSnorby iface = nbr->iface; 232978e5cffSnorby addr = nbr->addr; 233978e5cffSnorby } else { 234978e5cffSnorby /* multicast on interface */ 235978e5cffSnorby iface = xiface; 236*2471dd62Sflorian inet_pton(AF_INET, AllDVMRPRouters, &addr); 237978e5cffSnorby } 238978e5cffSnorby 239978e5cffSnorby while (!TAILQ_EMPTY(rr_list)) { 240e39620e5Snicm if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL) 241978e5cffSnorby fatal("rr_list_send"); 242978e5cffSnorby 243978e5cffSnorby prefixlen = 0; 244978e5cffSnorby while (((le = TAILQ_FIRST(rr_list)) != NULL) && 24535f7e862Sclaudio (ibuf_size(buf) < 1000)) { 246978e5cffSnorby /* netmask */ 247978e5cffSnorby netmask = le->re->mask.s_addr; 248978e5cffSnorby if (prefixlen != mask2prefixlen(netmask)) { 249978e5cffSnorby prefixlen = mask2prefixlen(netmask); 250acfb6403Snorby netmask = ntohl(netmask) << 8; 251acfb6403Snorby netmask = htonl(netmask); 252e39620e5Snicm ibuf_add(buf, &netmask, 3); 253978e5cffSnorby } 25413d89169Snorby netid_len = PREFIX_SIZE(prefixlen); 255978e5cffSnorby 256978e5cffSnorby /* netid */ 257978e5cffSnorby netid = le->re->net.s_addr; 258e39620e5Snicm ibuf_add(buf, &netid, netid_len); 259978e5cffSnorby 260978e5cffSnorby /* metric */ 261978e5cffSnorby if (iface->ifindex == le->re->ifindex) 262978e5cffSnorby /* poison reverse */ 263978e5cffSnorby metric = le->re->metric + INFINITY_METRIC; 264978e5cffSnorby else 265978e5cffSnorby metric = le->re->metric; 266978e5cffSnorby 267978e5cffSnorby /* 268978e5cffSnorby * determine if we need to flag last entry with current 269978e5cffSnorby * netmask. 270978e5cffSnorby */ 271978e5cffSnorby le2 = TAILQ_NEXT(le, entry); 272978e5cffSnorby if (le2 != NULL) { 273978e5cffSnorby if (mask2prefixlen(le2->re->mask.s_addr) != 274978e5cffSnorby prefixlen) 275978e5cffSnorby metric = metric | LAST_MASK; 276978e5cffSnorby } else { 277978e5cffSnorby metric = metric | LAST_MASK; 278978e5cffSnorby } 279978e5cffSnorby 280e39620e5Snicm ibuf_add(buf, &metric, sizeof(metric)); 281978e5cffSnorby 282978e5cffSnorby TAILQ_REMOVE(rr_list, le, entry); 28350b67c3fSmichele rr_list_remove(le->re); 284978e5cffSnorby free(le); 285978e5cffSnorby } 28635f7e862Sclaudio send_report(iface, addr, ibuf_data(buf), ibuf_size(buf)); 287e39620e5Snicm ibuf_free(buf); 288978e5cffSnorby } 289978e5cffSnorby } 290