1 /* $OpenBSD: igmp.c,v 1.4 2015/12/07 19:14:49 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 <arpa/inet.h> 23 #include <sys/time.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <event.h> 27 28 #include "igmp.h" 29 #include "dvmrpd.h" 30 #include "dvmrp.h" 31 #include "log.h" 32 #include "dvmrpe.h" 33 34 int igmp_chksum(struct igmp_hdr *); 35 36 /* IGMP packet handling */ 37 int 38 send_igmp_query(struct iface *iface, struct group *group) 39 { 40 struct igmp_hdr igmp_hdr; 41 struct sockaddr_in dst; 42 struct ibuf *buf; 43 int ret = 0; 44 45 log_debug("send_igmp_query: interface %s", iface->name); 46 47 if (iface->passive) 48 return (0); 49 50 if ((buf = ibuf_open(iface->mtu - sizeof(struct ip))) == NULL) 51 fatal("send_igmp_query"); 52 53 /* IGMP header */ 54 memset(&igmp_hdr, 0, sizeof(igmp_hdr)); 55 igmp_hdr.type = PKT_TYPE_MEMBER_QUERY; 56 57 if (group == NULL) { 58 /* general query - version is configured */ 59 igmp_hdr.grp_addr = 0; 60 61 switch (iface->igmp_version) { 62 case 1: 63 break; 64 case 2: 65 igmp_hdr.max_resp_time = iface->query_resp_interval; 66 break; 67 default: 68 fatal("send_igmp_query: invalid igmp version"); 69 } 70 } else { 71 /* group specific query - only version 2 */ 72 igmp_hdr.grp_addr = group->addr.s_addr; 73 igmp_hdr.max_resp_time = iface->last_member_query_interval; 74 } 75 76 /* update chksum */ 77 igmp_hdr.chksum = in_cksum(&igmp_hdr, sizeof(igmp_hdr)); 78 79 ibuf_add(buf, &igmp_hdr, sizeof(igmp_hdr)); 80 81 /* set destination address */ 82 dst.sin_family = AF_INET; 83 dst.sin_len = sizeof(struct sockaddr_in); 84 inet_aton(AllSystems, &dst.sin_addr); 85 86 ret = send_packet(iface, buf->buf, buf->wpos, &dst); 87 ibuf_free(buf); 88 return (ret); 89 } 90 91 void 92 recv_igmp_query(struct iface *iface, struct in_addr src, char *buf, 93 u_int16_t len) 94 { 95 struct igmp_hdr igmp_hdr; 96 struct group *group; 97 98 log_debug("recv_igmp_query: interface %s", iface->name); 99 100 if (len < sizeof(igmp_hdr)) { 101 log_debug("recv_igmp_query: invalid IGMP report, interface %s", 102 iface->name); 103 return; 104 } 105 106 memcpy(&igmp_hdr, buf, sizeof(igmp_hdr)); 107 iface->recv_query_resp_interval = igmp_hdr.max_resp_time; 108 109 /* verify chksum */ 110 if (igmp_chksum(&igmp_hdr) == -1) { 111 log_debug("recv_igmp_query: invalid chksum, interface %s", 112 iface->name); 113 return; 114 } 115 116 if (src.s_addr < iface->addr.s_addr && igmp_hdr.grp_addr == 0) { 117 /* we received a general query and we lost the election */ 118 if_fsm(iface, IF_EVT_QRECVD); 119 /* remember who is querier */ 120 iface->querier = src; 121 return; 122 } 123 124 if (iface->state == IF_STA_NONQUERIER && igmp_hdr.grp_addr != 0) { 125 /* validate group id */ 126 if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) { 127 log_debug("recv_igmp_query: invalid group, " 128 "interface %s", iface->name); 129 return; 130 } 131 132 if ((group = group_list_add(iface, igmp_hdr.grp_addr)) 133 != NULL) 134 group_fsm(group, GRP_EVT_QUERY_RCVD); 135 } 136 } 137 138 void 139 recv_igmp_report(struct iface *iface, struct in_addr src, char *buf, 140 u_int16_t len, u_int8_t type) 141 { 142 struct igmp_hdr igmp_hdr; 143 struct group *group; 144 145 log_debug("recv_igmp_report: interface %s", iface->name); 146 147 if (len < sizeof(igmp_hdr)) { 148 log_debug("recv_igmp_report: invalid IGMP report, interface %s", 149 iface->name); 150 return; 151 } 152 153 memcpy(&igmp_hdr, buf, sizeof(igmp_hdr)); 154 155 /* verify chksum */ 156 if (igmp_chksum(&igmp_hdr) == -1) { 157 log_debug("recv_igmp_report: invalid chksum, interface %s", 158 iface->name); 159 return; 160 } 161 162 /* validate group id */ 163 if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) { 164 log_debug("recv_igmp_report: invalid group, interface %s", 165 iface->name); 166 return; 167 } 168 169 if ((group = group_list_add(iface, igmp_hdr.grp_addr)) == NULL) 170 return; 171 172 if (iface->state == IF_STA_QUERIER) { 173 /* querier */ 174 switch (type) { 175 case PKT_TYPE_MEMBER_REPORTv1: 176 group_fsm(group, GRP_EVT_V1_REPORT_RCVD); 177 break; 178 case PKT_TYPE_MEMBER_REPORTv2: 179 group_fsm(group, GRP_EVT_V2_REPORT_RCVD); 180 break; 181 default: 182 fatalx("recv_igmp_report: unknown IGMP report type"); 183 } 184 } else { 185 /* non querier */ 186 group_fsm(group, GRP_EVT_REPORT_RCVD); 187 } 188 } 189 190 void 191 recv_igmp_leave(struct iface *iface, struct in_addr src, char *buf, 192 u_int16_t len) 193 { 194 struct igmp_hdr igmp_hdr; 195 struct group *group; 196 197 log_debug("recv_igmp_leave: interface %s", iface->name); 198 199 if (iface->state != IF_STA_QUERIER) 200 return; 201 202 if (len < sizeof(igmp_hdr)) { 203 log_debug("recv_igmp_leave: invalid IGMP leave, interface %s", 204 iface->name); 205 return; 206 } 207 208 memcpy(&igmp_hdr, buf, sizeof(igmp_hdr)); 209 210 /* verify chksum */ 211 if (igmp_chksum(&igmp_hdr) == -1) { 212 log_debug("recv_igmp_leave: invalid chksum, interface %s", 213 iface->name); 214 return; 215 } 216 217 /* validate group id */ 218 if (!IN_MULTICAST(ntohl(igmp_hdr.grp_addr))) { 219 log_debug("recv_igmp_leave: invalid group, interface %s", 220 iface->name); 221 return; 222 } 223 224 if ((group = group_list_find(iface, igmp_hdr.grp_addr)) != NULL) { 225 group_fsm(group, GRP_EVT_LEAVE_RCVD); 226 } 227 } 228 229 int 230 igmp_chksum(struct igmp_hdr *igmp_hdr) 231 { 232 u_int16_t chksum; 233 234 chksum = igmp_hdr->chksum; 235 igmp_hdr->chksum = 0; 236 237 if (chksum != in_cksum(igmp_hdr, sizeof(*igmp_hdr))) 238 return (-1); 239 240 return (0); 241 } 242