1#include "defs.h" 2 3struct source_set ZEROSET; 4 5struct source_set_op 6{ 7 void (*op)(struct source_set *, struct source_set *, struct source_set *, int); 8 int inverse; 9 int mode_change; 10}; 11 12static struct source_set_op host_op[st_max][ev_max] = 13{ 14 /* none operation (initial)*/ 15 {{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}}, 16 /* IS_IN operation*/ 17 {{NULL, 0, 0}, {set_assign, 0, 0}, {set_assign, 0, 1}, {set_assign, 0, 0}, {set_assign, 0, 1}, {set_add, 0, 0}, {set_subtract, 0, 0}}, 18 /* IS_EX operation*/ 19 {{NULL, 0, 0}, {set_assign, 0, 1}, {set_assign, 0, 0}, {set_assign, 0, 1}, {set_assign, 0, 0}, {set_subtract, 0, 0}, {set_add, 0, 0}} 20}; 21 22static struct source_set_op proxy_op[st_max][ev_max] = 23{ 24 /* none operation (initial)*/ 25 {{NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}}, 26 /* IS_IN operation*/ 27 {{NULL, 0, 0}, {set_add, 0, 0}, {set_subtract, 1, 1}, {set_add, 0, 0}, {set_subtract, 1, 1}, {NULL, 0, 0}, {NULL, 0, 0}}, 28 /* IS_EX operation*/ 29 {{NULL, 0, 0}, {set_subtract, 0, 0}, {set_intersection, 0, 0}, {set_subtract, 0, 0}, {set_intersection, 0, 0}, {NULL, 0, 0}, {NULL, 0, 0}} 30}; 31 32/* 33 * add_ebt_rule : add a ebtables' rule 34 * port : insert to which chain 35 * group : wanted multicast group address 36 * newmode : include or exclude mode 37 * newset : new source list set 38*/ 39static inline void 40add_ebt_rule(uint32 port, uint32 group, uint32 newmode, struct source_set *newset) 41{ 42 char line[256]; 43 int i, len; 44 int num = (newset->num == PORT_INIT_STATE)?0:newset->num; 45 if (newset) 46 { 47 len = sprintf(line, "ebtables -I OUTPUT -o ethwan -p ipv4 --ip-dst %u.%u.%u.%u ", 48 NIPQUAD(group)); 49 if (num) 50 len += sprintf(line+len, "%s --ip-msip ", (newmode == st_is_in)?"":"!"); 51 for (i = 0; i < num; i++) 52 len += sprintf(line+len, "%u.%u.%u.%u%s", 53 NIPQUAD(newset->list[i]), (i == (num-1))?"":","); 54 sprintf(line+len, " -j ACCEPT"); 55 DPRINTF("add_ebt_rule :: cmd [%s]\n", line); 56 system(line); 57 } 58} 59 60/* 61 * del_ebt_rule : del a ebtables' rule 62 * port : insert to which chain 63 * group : wanted multicast group address 64 * oldmode : include or exclude mode 65 * oldset : old source list set 66*/ 67static inline void 68del_ebt_rule(uint32 port, uint32 group, uint32 oldmode, struct source_set *oldset) 69{ 70 char line[256]; 71 int i, len; 72 int num = (oldset->num == PORT_INIT_STATE)?0:oldset->num; 73 if (oldset) 74 { 75 len = sprintf(line, "ebtables -D OUTPUT -o ethwan -p ipv4 --ip-dst %u.%u.%u.%u ", 76 NIPQUAD(group), (oldmode == st_is_in)?"":"!"); 77 if (num) 78 len += sprintf(line+len, "%s --ip-msip ", (oldmode == st_is_in)?"":"!"); 79 for (i = 0; i < num; i++) 80 len += sprintf(line+len, "%u.%u.%u.%u%s", 81 NIPQUAD(oldset->list[i]), (i != (num-1))?",":""); 82 sprintf(line+len, " -j ACCEPT"); 83 DPRINTF("del_ebt_rule :: cmd [%s]\n", line); 84 system(line); 85 } 86} 87 88/* 89 * update_ebt_rules : update a ebtables' rule 90 * port : insert to which chain 91 * group : wanted multicast group address 92 * oldmode : include or exclude mode 93 * oldset : old source list set 94 * newmode : include or exclude mode 95 * newset : new source list set 96 * note : if our update procedure causes the media stream discontinuously, we may swap the update commands 97*/ 98static inline void 99update_ebt_rules(uint32 port, uint32 group, uint32 oldmode, struct source_set *oldset, uint32 newmode, struct source_set *newset) 100{ 101 del_ebt_rule(port, group, oldmode, oldset); 102 103 add_ebt_rule(port, group, newmode, newset); 104} 105 106static inline void 107set_mcast_set(struct Listener *listener, uint32 group) 108{ 109 char line[256], i, len; 110 len = sprintf(line, "echo \"%u.%u.%u.%u %u.%u.%u.%u %d %d ", 111 NIPQUAD(listener->srcAddr), NIPQUAD(group), (listener->mode == st_is_in)?1:0, listener->source.num); 112 for (i = 0; i < listener->source.num; i++) 113 len += sprintf(line+len, "%u.%u.%u.%u ", NIPQUAD(listener->source.list[i])); 114 sprintf(line+len, "\" > /proc/mcast_set"); 115 system(line); 116} 117 118/* 119 * vaild_grec : to valid the group record 120 * if the group record is valid/invalid, to return true/false. 121 * if the multicast address field is not a multicast address ==> invalid 122 * if the number of source is greater than 8 ==> invalid (for Netgear Spec.) 123 * if the sources have more than one zero address or sources have multicast address ==> invalid 124*/ 125static inline int 126vaild_grec(struct igmpv3_grec *grec) 127{ 128 int i, flag =0; 129 130 if (!MULTICAST(grec->grec_mca) || 131 (grec->grec_nsrcs > 8)|| 132 (grec->grec_type > IGMPV3_BLOCK_OLD_SOURCE)) 133 return 0; 134 for (i = 0; i < grec->grec_nsrcs; i++) 135 { 136 if (!grec->grec_src[i]) 137 flag++; 138 if (MULTICAST(grec->grec_src[i])) 139 flag += 2; 140 if (flag > 1) 141 return 0; 142 } 143 return 1; 144} 145 146/* 147 * copy_source_list : copy group record to the temporary source set buffer 148 * set : the source set storage buffer 149 * grec : the group record 150*/ 151static inline void 152copy_source_list(struct source_set *set, struct igmpv3_grec *grec) 153{ 154 int i; 155 memset(set, 0, sizeof(struct source_set)); 156 set->num = grec->grec_nsrcs; 157 for (i = 0; i < set->num; i++) 158 set->list[i] = grec->grec_src[i]; 159} 160 161/* 162 * update_proxy_source_info : update the source set of the router 163 * note : port 0 means wireless port, 164 * port1 ~ portN mean ethernet ports 165 * to collect all ports source set information and to calculate the latest source set 166 * if source set is changed, to use set_source_list to update the source set. 167*/ 168static inline void 169update_proxy_source_info(struct RouteTable *croute) 170{ 171 int i; 172 struct source_set_op *proxy; 173 struct source_set tmp, tmp1; 174 uint32 mode = croute->port_mode[0]; 175 176 memcpy(&tmp, &croute->port_source[0], sizeof(struct source_set)); 177 memset(&tmp1, 0, sizeof(struct source_set)); 178 for (i = 1; i <= PORT_MAX_NUM; i++) 179 { 180 proxy = &proxy_op[mode][croute->port_mode[i]]; 181 proxy->op(&tmp, &croute->port_source[i], &tmp1, proxy->inverse); 182 if (proxy->mode_change) 183 mode = (mode== st_is_in)?st_is_ex:st_is_in; 184 memcpy(&tmp, &tmp1, sizeof(struct source_set)); 185 } 186 187 if (mode != croute->mode || 188 set_comp(&croute->source, &tmp)) 189 { 190 croute->mode = mode; 191 memcpy(&croute->source, &tmp, sizeof(struct source_set)); 192 set_source_list(croute); 193 } 194 195} 196 197/* 198 * update_port_source_info : update the source set of the specific port 199 * croute : multicast routing table info 200 * port : come from which port 201 * flag : call update_proxy_source_info or not 202 * note : if the source set is changed to the include mode with an empty set, to delete the ebtables' rule 203*/ 204static inline void 205update_port_source_info(struct RouteTable *croute, uint32 port, uint32 flag) 206{ 207 struct source_set tmp[3]; 208 uint32 i = 0, curr_state = st_is_in, next_state; 209 struct Listener *listener = croute->listeners, *next; 210 struct source_set_op *proxy; 211 212 memset(&tmp[0], 0, sizeof(struct source_set)); 213 tmp[0].num = PORT_INIT_STATE; 214 if (listener) 215 { 216 while (listener) 217 { 218 next = listener->nextlistener; 219 if (listener->port == port) 220 { 221 if (tmp[0].num == PORT_INIT_STATE) 222 { 223 curr_state = listener->mode; 224 memcpy(&tmp[0], &listener->source, sizeof(struct source_set)); 225 } 226 else 227 { 228 memcpy(&tmp[1], &listener->source, sizeof(struct source_set)); 229 next_state = listener->mode; 230 proxy = &proxy_op[curr_state][next_state]; 231 proxy->op(&tmp[0], &tmp[1], &tmp[2], proxy->inverse); 232 memcpy(&tmp[0], &tmp[2], sizeof(struct source_set)); 233 if (proxy->mode_change) 234 curr_state = (curr_state == st_is_in)?st_is_ex:st_is_in; 235 } 236 } 237 listener = next; 238 } 239 } 240 241 if (croute->port_mode[port] != curr_state 242 ||set_comp(&croute->port_source[port], &tmp[0])) 243 { 244 if (tmp[0].num == PORT_INIT_STATE) 245 { 246 del_ebt_rule(port, croute->group, croute->port_mode[port], &croute->port_source[port]); 247 memcpy(&croute->port_source[port], tmp, sizeof(struct source_set)); 248 croute->port_mode[port] = st_is_in; 249 } 250 else 251 { 252 update_ebt_rules(port, croute->group, croute->port_mode[port], &croute->port_source[port], curr_state, &tmp[0]); 253 memcpy(&croute->port_source[port], &tmp[0], sizeof(struct source_set)); 254 croute->port_mode[port] = curr_state; 255 } 256 if (flag) 257 update_proxy_source_info(croute); 258 } 259} 260 261/* 262 * update_all_ports : update all ports information 263 * croute : multicast routing table info 264 * If the clients join(leave) or the port status is changed, 265 * we will call update_all_ports. 266 * And to avoid the update_proxy_source_info is called frequently 267 * so we will call update_proxy_source_info after all ports are updated. 268*/ 269void 270update_all_ports(struct RouteTable *croute) 271{ 272 int i; 273 if (mc_ctrl_packet(ntohl(croute->group))) 274 return; 275 276 for (i = 0; i <= PORT_MAX_NUM; i++) 277 update_port_source_info(croute, i, (i == PORT_MAX_NUM)?1:0); 278} 279 280/* 281 * add_listener : get a new client 282 * port : come from which port 283 * croute : multicast routing table info 284 * src : the ip address of the new client 285 * tmp1 : the new source set 286 * mode : include or exclude mode 287*/ 288static inline void 289add_listener(uint32 port, struct RouteTable *croute, uint32 src, struct source_set *tmp1, int mode) 290{ 291 struct Listener *listener = NULL; 292 struct source_set_op *proxy; 293 struct source_set tmp2; 294 295 listener = (struct Listener *)insertListener(port, croute, src); 296 if (!listener) 297 return; 298 299 listener->mode = mode; 300 listener->port = port; 301 listener->version = IGMP_V3_MEMBERSHIP_REPORT; 302 memcpy(&listener->source, tmp1, sizeof(struct source_set)); 303 304 update_port_source_info(croute, port, 1); 305 if (!port) 306 { 307 join_group(src, croute->group); 308 set_mcast_set(listener, croute->group); 309 } 310} 311 312/* 313 * create_croute : create a new multicast routing table entry 314 * group : multicast group address 315 * src : the ip address of the new client 316 * port : come from which port 317 * grec_type : the type of group record 318 * source : the new source set 319 * note : In order to pass the cdrouter test case (case 335 ~ case 343). 320 * If we get any source address, to call add_source_list first before update the source list. 321*/ 322static inline void 323create_croute(uint32 group, uint32 src, uint32 port, uint8 grec_type, struct source_set *source) 324{ 325 struct RouteTable *croute = NULL; 326 struct Listener *listener = NULL; 327 uint32 mode; 328 acceptGroupReport(port, src, group, IGMP_V3_MEMBERSHIP_REPORT); 329 croute = (struct RouteTable *)findRoute(group); 330 331 if (!croute) 332 return; 333 memcpy(&croute->source, source, sizeof(struct source_set)); 334 mode = (grec_type == IGMPV3_MODE_IS_INCLUDE || 335 grec_type == IGMPV3_CHANGE_TO_INCLUDE|| 336 grec_type == IGMPV3_ALLOW_NEW_SOURCE)?st_is_in:st_is_ex; 337 listener = (struct Listener *)findListener(croute, src); 338 if (listener) 339 { 340 memcpy(&listener->source, source, sizeof(struct source_set)); 341 listener->mode = mode; 342 listener->port = port; 343 listener->version = IGMP_V3_MEMBERSHIP_REPORT; 344 if (listener->source.num) 345 add_source_list(croute->group, listener->source.list[0]); 346 } 347 update_port_source_info(croute, port, 1); 348 if (!port) 349 set_mcast_set(listener, croute->group); 350} 351 352/* 353 * processV3report : IGMPV3 main function 354 * buf : igmp packet 355 * src : the ip address of the new client 356 * port : come from which port 357*/ 358void 359processV3report(char *buf, uint32 src, uint32 port) 360{ 361 struct igmpv3_report *igmpv3 = (struct igmpv3_report *)buf; 362 uint16 i, num = igmpv3->ngrec; 363 struct igmpv3_grec *grec = NULL; 364 uint32 group, mode, port_change = 0; 365 struct RouteTable *croute = NULL; 366 struct source_set tmp1, tmp2; 367 struct Listener *listener = NULL; 368 struct source_set_op *host; 369 struct IfDesc *sourceVif; 370 char *pdata = (char *)igmpv3->grec; 371 372 for (i = 0;(i < num) && pdata; i++, pdata += grec->grec_nsrcs*4 + 8) 373 { 374 grec = (struct igmpv3_grec *)pdata; 375 // check group record is valid or not 376 if (!vaild_grec(grec)) 377 return; 378 379 group = grec->grec_mca; 380 if (mc_ctrl_packet(ntohl(group))) 381 continue; 382 croute = (struct RouteTable *)findRoute(group); 383 copy_source_list(&tmp1, grec); 384 if (!croute) 385 { 386 // if the multicast group entry is not existed and group record type is not block old sources, 387 // to add a new multicast group entry 388 if (grec->grec_type < IGMPV3_BLOCK_OLD_SOURCE) 389 create_croute(group, src, port, grec->grec_type, &tmp1); 390 } 391 else 392 { 393 // it should not be happened 394 // to make sure all router mode will be include, exclude or not set. 395 if (croute->mode > 2) 396 croute->mode = 0; 397 398 listener = (struct Listener *)findListener(croute, src); 399 // it should not be happened 400 if (croute->mode == st_none) 401 { 402 switch (grec->grec_type) 403 { 404 case ev_is_in: 405 case ev_to_in: 406 case ev_allow: 407 // skipped leave message 408 if (!memcmp(&tmp1, &ZEROSET, sizeof(struct source_set))) 409 break; 410 case ev_is_ex: 411 case ev_to_ex: 412 create_croute(group, src, port, grec->grec_type, &tmp1); 413 case ev_block: 414 default: 415 break; 416 } 417 continue; 418 } 419 sourceVif = getIfByAddress( src ); 420 if(!sourceVif) 421 return; 422 // to update kernel multicast route table 423 insertRoute(port, group, sourceVif->index, src); 424 if (listener) 425 { 426 // to check port is changed or not 427 if (listener->port != port) 428 port_change = 1; 429 host = &host_op[listener->mode][grec->grec_type]; 430 host->op(&listener->source, &tmp1, &tmp2, host->inverse); 431 switch (FSM(listener->mode, grec->grec_type)) 432 { 433 case FSM(st_is_in, ev_is_in): 434 case FSM(st_is_in, ev_to_in): 435 case FSM(st_is_in, ev_allow): 436 if (port_change || memcmp(&listener->source, &tmp2, sizeof(struct source_set))) 437 { 438 // if the source set is become include mode with an empty set, 439 // to call acceptLeaveMessage 440 if (tmp2.num) 441 { 442 memcpy(&listener->source, &tmp2, sizeof(struct source_set)); 443 listener->port = port; 444 if (port_change) 445 update_all_ports(croute); 446 else 447 update_port_source_info(croute, port, 1); 448 if (!port) 449 set_mcast_set(listener, group); 450 } 451 else 452 acceptLeaveMessage(src, group); 453 } 454 break; 455 case FSM(st_is_in, ev_is_ex): 456 case FSM(st_is_in, ev_to_ex): 457 listener->mode = st_is_ex; 458 memcpy(&listener->source, &tmp2, sizeof(struct source_set)); 459 listener->port = port; 460 if (port_change) 461 update_all_ports(croute); 462 else 463 update_port_source_info(croute, port, 1); 464 if (!port) 465 set_mcast_set(listener, group); 466 break; 467 case FSM(st_is_in, ev_block): 468 if (tmp2.num) 469 { 470 if (port_change || memcmp(&listener->source, &tmp2, sizeof(struct source_set))) 471 { 472 memcpy(&listener->source, &tmp2, sizeof(struct source_set)); 473 listener->port = port; 474 if (port_change) 475 update_all_ports(croute); 476 else 477 update_port_source_info(croute, port, 1); 478 if (!port) 479 set_mcast_set(listener, group); 480 } 481 } 482 else 483 acceptLeaveMessage(src, group); 484 break; 485 case FSM(st_is_ex, ev_is_in): 486 case FSM(st_is_ex, ev_to_in): 487 if (tmp2.num) 488 { 489 listener->port = port; 490 listener->mode = st_is_in; 491 if (memcmp(&listener->source, &tmp2, sizeof(struct source_set))) 492 memcpy(&listener->source, &tmp2, sizeof(struct source_set)); 493 if (port_change) 494 update_all_ports(croute); 495 else 496 update_port_source_info(croute, port, 1); 497 if (!port) 498 set_mcast_set(listener, group); 499 } 500 else 501 acceptLeaveMessage(src, group); 502 break; 503 case FSM(st_is_ex, ev_is_ex): 504 case FSM(st_is_ex, ev_to_ex): 505 case FSM(st_is_ex, ev_allow): 506 case FSM(st_is_ex, ev_block): 507 if (port_change || memcmp(&listener->source, &tmp2, sizeof(struct source_set))) 508 { 509 listener->port = port; 510 memcpy(&listener->source, &tmp2, sizeof(struct source_set)); 511 if (port_change) 512 update_all_ports(croute); 513 else 514 update_port_source_info(croute, port, 1); 515 if (!port) 516 set_mcast_set(listener, group); 517 } 518 break; 519 default: 520 DPRINTF("%s :: unknown state [%d] or unknown event [%d]", __FUNCTION__, croute->mode, grec->grec_type); 521 break; 522 } 523 } 524 else 525 { 526 switch (grec->grec_type) 527 { 528 case ev_is_in: 529 case ev_to_in: 530 case ev_allow: 531 mode = st_is_in; 532 break; 533 case ev_is_ex: 534 case ev_to_ex: 535 mode = st_is_ex; 536 break; 537 // if an unknown client send a block type message, 538 // to skip it 539 case ev_block: 540 default: 541 continue; 542 } 543 add_listener(port, croute, src, &tmp1, mode); 544 } 545 } 546 } 547} 548 549 550