int simquit(void * pio) { PACKET pkt; ip_addr hop; struct ip * pip; if(simtimer) { in_timerkill(simtimer); simtimer = 0; } /* send all queued packets */ while(simq.q_head) { pkt = (PACKET)getq(&simq); /* get pkt to send */ hop = pkt->fhost; /* get hop from host field */ pip = ip_head(pkt); /* get real host address */ pkt->fhost = pip->ip_dest; /* restore host field */ ip2mac(pkt, hop); /* send pkt to hardware */ } rfsim_routing = FALSE; ns_printf(pio, "rfsim stopped\n"); return 0; }
void rfsim_timer() { ip_addr hop; struct ip * pip; PACKET pkt; /* send al packets which have timed out */ while(((PACKET)(simq.q_head))->nb_tstamp < cticks) { pkt = (PACKET)getq(&simq); /* get pkt to send */ hop = pkt->fhost; /* get hop from host field */ pip = ip_head(pkt); /* get real host address */ pkt->fhost = pip->ip_dest; /* restore host field */ simpkts++; /* see if it's time to drop a packet */ if((lossrate) && ((simpkts - simlastloss) > lossrate) && ((cticks & deviation) == 1)) { LOCK_NET_RESOURCE(FREEQ_RESID); pk_free(pkt); /* drop instead of send */ UNLOCK_NET_RESOURCE(FREEQ_RESID); simlastloss = simpkts; /* reset drop timer */ simdrops++; /* count drops */ } else ip2mac(pkt, hop); /* send pkt to hardware */ } return; }
int igmpv2_input (PACKET p) { struct igmp * igmp; struct ip * pip; int igmplen; u_char type; int rc; pip = ip_head (p); /* compute length of IGMP packet (after accounting for IP header, * including the IP Router Alert option (if present)) */ igmplen = p->nb_plen - ip_hlen (pip); igmp = (struct igmp *) (ip_data (pip)); /* extract the IGMP packet type from received packet */ type = igmp->igmp_type; switch (type) { case IGMP_HOST_MEMBERSHIP_QUERY: rc = igmpv2_process_query (p); break; case IGMP_HOST_MEMBERSHIP_REPORT: case IGMPv2_MEMBERSHIP_REPORT: rc = igmpv2_process_report (p); break; case IGMPv2_LEAVE_GROUP: /* Leave messages are typically addressed to the all-routers * multicast address group (224.0.0.2). We do not implement * multicast router functionality, and therefore, do not * expect to receive such messages. However, according to * RFC 2236, some implementations of an older version of the * IGMPv2 specification send leave messages to the group * being left. If we do receive such a message, we will * drop it. */ ++igmpstats.igmpv2mode_v2_leave_msgs_rcvd; rc = IGMP_OK; break; default: ++igmpstats.igmpv2mode_unknown_pkttype; rc = IGMP_ERR; break; } /* end SWITCH */ /* we're done processing the received packet; return packet buffer * back to free pool */ LOCK_NET_RESOURCE(FREEQ_RESID); pk_free(p); UNLOCK_NET_RESOURCE(FREEQ_RESID); return rc; }
int igmp_validate (PACKET p) { struct ip * pip; int igmplen; struct igmp * igmp; u_short osum; u_short xsum; u_char type; ip_addr mcgrp_addr; u_char resp_time; pip = ip_head (p); /* compute length of IGMP packet (after accounting for IP header, * including the IP Router Alert option (if present)) */ igmplen = p->nb_plen - ip_hlen (pip); /* validate length (IGMP_MINLEN is 8 bytes) */ if (igmplen != IGMP_MINLEN) { ++igmpstats.igmp_badlen_rcvd; return ENP_BAD_HEADER; } /* validate checksum */ igmp = (struct igmp *) (ip_data (pip)); osum = igmp->igmp_cksum; igmp->igmp_cksum = 0; xsum = ~cksum(igmp, igmplen>>1); if (xsum != osum) { igmp->igmp_cksum = osum; ++igmpstats.igmp_badsum_rcvd; return ENP_BAD_HEADER; } /* extract the IGMP packet type, Group Address, and Max Response Time * (unused for IGMPv1) fields from received packet */ type = igmp->igmp_type; mcgrp_addr = ntohl(igmp->igmp_group); resp_time = igmp->igmp_code; if (type == IGMP_HOST_MEMBERSHIP_QUERY) { if ((resp_time == 0) || /* IGMPv1 Query */ ((resp_time > 0) && (mcgrp_addr == 0))) /* IGMPv2 General Query */ { /* if this is a IGMPv1 Host Membership Query or a IGMPv2 * General Query, it must be addressed to the all-hosts * group */ if (pip->ip_dest != igmp_all_hosts_group) { ++igmpstats.igmp_bad_queries_rcvd; return ENP_BAD_HEADER; } } if ((resp_time > 0) && (mcgrp_addr != 0)) { /* this is a IGMPv2 Group-Specific Query. */ if (p->net->igmp_oper_mode == IGMP_MODE_V1) { /* IGMPv1 code does not understand a IGMPv2 Group- * Specific Query */ return ENP_BAD_HEADER; } /* check to make sure that the group address field carries * a valid multicast address; if it doesn't, we * drop the packet. Also drop packets that * carry the multicast address for the all-hosts * group. */ if ((!IN_MULTICAST(mcgrp_addr)) || /* igmp_all_hosts_group is already in network byte order */ (igmp->igmp_group == igmp_all_hosts_group)) { ++igmpstats.igmpv2mode_v2_bad_grp_specific_queries_rcvd; /* caller will free received packet */ return ENP_BAD_HEADER; } } } /* check to ensure that a received IGMPv1 or v2 Report has the * same IP host group address in its IP destination field and * its IGMP group address field, and that the group address is * a valid multicast address */ if ((type == IGMP_HOST_MEMBERSHIP_REPORT) || (type == IGMPv2_MEMBERSHIP_REPORT)) { if ((igmp->igmp_group != pip->ip_dest) || (!IN_MULTICAST(mcgrp_addr))) { ++igmpstats.igmp_bad_reports_rcvd; return ENP_BAD_HEADER; } } /* * After receiving an IGMP packet, our IGMP module will check for the * presence of the Router Alert option in the following types of * packets. Packets that do not have the option will be discarded. * (a) Version 2 General Query (0x11) (differentiated from a Version 1 Host Membership Query by having a Max Response Time > 0) * (b) Version 2 Group-Specific Query (0x11) * (c) Version 2 Membership Report (0x16) * (d) Version 2 Leave Group (0x17) * Version 1 Host Membership Reports and Version 1 Host Membership Query * packets will not be checked for the IP Router Alert option. */ #ifdef IGMP_V2 if ((type == IGMPv2_LEAVE_GROUP) || (type == IGMPv2_MEMBERSHIP_REPORT) || ((type == IGMP_HOST_MEMBERSHIP_QUERY) && (igmp->igmp_code > 0))) { if (!igmpv2_chk4_rtr_alert_opt (pip)) { ++igmpstats.igmpv2mode_v2_rtr_alert_missing; return ENP_BAD_HEADER; } } #endif /* validation successful */ return IGMP_OK; }
int igmpv2_process_query (PACKET p) { struct igmp * igmp; struct ip * pip; NET netp; u_short max_resp_time; u_char process_all; struct in_multi * inm; ip_addr mcgrp_addr; netp = p->net; pip = ip_head (p); igmp = (struct igmp *) (ip_data (pip)); mcgrp_addr = ntohl(igmp->igmp_group); if (igmp->igmp_code == 0) { /* this is a IGMPv1 Host Membership Query */ netp->igmpv1_rtr_present = IGMP_TRUE; netp->igmpv1_query_rcvd_time = cticks; ++igmpstats.igmpv2mode_v1_queries_rcvd; /* set maximum time to respond to the equivalent of 10 * seconds worth of "ticks" (the timeout routine is * intended to be invoked PR_FASTHZ (5) times a second, * so each tick is equal to 200 ms) */ max_resp_time = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; process_all = IGMP_TRUE; } else { /* this is either a IGMPv2 General Query or * a IGMPv2 Group-Specific Query */ if (igmp->igmp_group == 0) { /* this is a IGMPv2 General Query */ ++igmpstats.igmpv2mode_v2_general_queries_rcvd; process_all = IGMP_TRUE; } else { /* this is a IGMPv2 Group-Specific Query */ ++igmpstats.igmpv2mode_v2_grp_specific_queries_rcvd; process_all = IGMP_FALSE; } /* irrespective of whether received message is a * IGMPv2 General Query or a IGMPv2 Group-Specific Query, * set maximum time to respond to value extracted * from received message. The value in the message * is in tenths of a second. max_resp_time is in * units of ticks (where one tick is 200 ms) */ max_resp_time = (igmp->igmp_code * PR_FASTHZ) / 10; } /* process all entries in a link's multicast address linked * list (pointed to by mc_list) as part of the response to * the received IGMPv1 Host Membership Query or IGMPv2 General * Query message */ if (process_all) { for (inm = netp->mc_list; inm; inm = inm->inm_next) { /* skip all IPv6 entries - they are indicated by * an IPv4 address field of 0 */ if (!(inm->inm_addr)) continue; /* skip IPv4 multicast address of 224.0.0.1 (note that * the IPv4 address stored in inm_addr is in network * byte order */ if (inm->inm_addr != igmp_all_hosts_group) igmpv2_chk_set_timer (inm, max_resp_time); } /* end FOR (iterate thru' mc_list on net) */ } /* end IF (process all) */ else { /* process one (for IGMPv2 Group-Specific Query) entry (the * one that corresponds to the address listed in the received * query) - it should be present in the link's multicast * address list */ inm = lookup_mcast(igmp->igmp_group, netp); if (inm != NULL) igmpv2_chk_set_timer (inm, max_resp_time); else ++igmpstats.igmpv2mode_v2_unknown_grp_specific_queries_rcvd; } /* end ELSE (process ALL) */ /* return success; caller will the received packet back to the * free pool */ return IGMP_OK; }
int igmpv2_process_report (PACKET p) { struct igmp * igmp; struct ip * pip; NET netp; struct in_multi * inm; netp = p->net; pip = ip_head (p); igmp = (struct igmp *) (ip_data (pip)); /* If we receive a IGMP (v1 or v2) report for a multicast group * that we have a timer running for, the IGMPv2 specification * requires that we stop the timer (and thereby cancel the * future transmission of our report). * However, we will use the following table to guide our actions * instead. Scenario #4 causes us to not cancel the timer, * since we have received a IGMPv2 report, but we believe that * the querier on the network is running IGMPv1, and therefore * will not be able to understand the IGMPv2 report. As a * result, we let our timer run, and if it expires, we will * send out a report. * The type of a received report can be determined by examining * the first byte of the IGMP message. This byte is 0x12 for a * IGMPv1 Host Membership Report, and 0x16 for a Version 2 * Membership Report. * Scenario# igmpv1_rtr_present Type of rcvd report Cancel timer * ============================================================= * 1 No IGMPv1 Yes * 2 No IGMPv2 Yes * 3 Yes IGMPv1 Yes * 4 Yes IGMPv2 No * In scenario #1 and #2, we have a IGMPv2-capable router that * can understand both IGMPv1 and IGMPv2 reports. In scenario * #3, we have a IGMPv1-capable router that can understand IGMPv1 * reports. In scenario #4, we have a IGMPv1-capable router that * cannot understand a IGMPv2 report. It is possible that the * IGMPv1-capable router in scenario #4 is also capable of * processing IGMPv2 packets (it has "downgraded" itself because * there are IGMPv1 routers on that network); however, we do not * know that, and hence we don't cancel our timer (for the * subsequent transmission of a IGMPv1 report). */ inm = lookup_mcast(igmp->igmp_group, netp); if (inm != NULL) { if (inm->inm_timer != 0) { /* we have a timer running */ if (!(netp->igmpv1_rtr_present && igmp->igmp_type == IGMPv2_MEMBERSHIP_REPORT)) { /* cancel timer */ inm->inm_timer = 0; /* decrement the count of running timers */ --igmp_timers_are_running; /* indicate that we are not the last host to send a * report for this group */ inm->last2send_report = IGMP_FALSE; ++igmpstats.igmpv2mode_v12_reports_rcvd_canceled_timer; } } else { /* we don't have a timer running; perhaps the source * host has just joined the group, and has sent an * unsolicited report */ ++igmpstats.igmpv2mode_v12_reports_rcvd_no_timer; } } else { /* since the Reports are sent the group address, we should * never receive them unless we are a member of that group * on that interface. Even if imperfect filtering at the * device level causes reports for unregistered groups to * be passed up to the IP module, ip_rcv_phase2 () is * responsible for dropping them, and so we should never * receive such packets. */ ++igmpstats.igmpv2mode_v12_unknown_grp_reports_rcvd; } return IGMP_OK; }
void so_icmpdu(PACKET p, struct destun * pdp) { ip_addr lhost; /* IP address of originator (our iface) */ ip_addr fhost; /* IP address we sent to */ unshort fport; /* TCP/UDP port we sent to */ unshort lport; /* TCP/UDP port we sent from */ struct inpcb * inp; struct socket * so; struct tcpcb * tp; /* extract information about packet which generated DU */ fhost = htonl(pdp->dip.ip_dest); lhost = htonl(pdp->dip.ip_src); lport = htons(*(unshort*)(&pdp->ddata[0])); fport = htons(*(unshort*)(&pdp->ddata[2])); #ifndef IP_PMTU /* if it's a datagram-too-big message, ignore it -- As the * build isn't using PMTU Discovery this packet is most * probably a Denial of Service Attack. */ if(pdp->dcode == DSTFRAG) { goto done; } #endif /* IP_PMTU */ /* if it's a TCP connection, clean it up */ if (pdp->dip.ip_prot == TCPTP) { /* find associated data structs and socket */ inp = in_pcblookup(&tcb, fhost, fport, lhost, lport, INPLOOKUP_WILDCARD); if (inp == 0) goto done; so = inp->inp_socket; if (so == 0) goto done; tp = intotcpcb(inp); if (tp) { if (tp->t_state <= TCPS_LISTEN) { goto done; } #ifdef ICMP_TCP_DOS { struct ip * pip; struct tcpiphdr * ti; pip = ip_head(p); /* find IP header */ ti = (struct tcpiphdr *)p->nb_prot; if(!((tp->snd_una <= ti->ti_seq) && (ti->ti_seq <= tp->snd_nxt))) goto done; /* If we get an ICMP Type 3 (Destination Unreachable) - Code 2 * (Protocol Unreachable) message and during the life of a TCP * connection, then its most probably a Denial of Service Attack. * As the only other interpretation would be that the support for * the transport protocol has been removed from the host sending * the error message during the life of the corresponding * connection. As in common practice this is higly unlikely in most * cases, we will treat this message as a DOS attack. */ if(pdp->dcode == DSTPROT) { if((tp->t_state >= TCPS_ESTABLISHED) && (tp->t_state <= TCPS_TIME_WAIT)) goto done; } /* Note some ICMP error messages generated by intermediate routers, * include more than the recommended 64 bits of the IP Data. If the * TCP ACK number happens to be present then use it in detecting a * Denial of Service attack. * * This way we can ensure that the TCP Acknowledgement number should * correspond to data that have already been acknowledged. This way * we can further reduce the possiblity of considering a spoofed ICMP * packet by a factor of 2. */ if(pip->ip_len >= 32) { if(!(ti->ti_seq <= tp->rcv_nxt)) goto done; } } #endif tcp_close(tp); } so->so_error = ECONNREFUSED; /* set error for socket owner */ } #ifdef UDP_SOCKETS /* this sockets layer supports UDP too */ else if(pdp->dip.ip_prot == UDP_PROT) { UDPCONN tmp; /* search udp table (which keeps hosts in net endian) */ for (tmp = firstudp; tmp; tmp = tmp->u_next) if ((tmp->u_fport == fport || tmp->u_fport == 0) && (tmp->u_fhost == htonl(fhost)) && (tmp->u_lport == lport)) { break; /* found our UDP table entry */ } if (!tmp) goto done; so = (struct socket *)tmp->u_data; /* May be non-socket (lightweight) UDP connection. */ if (so->so_type != SOCK_DGRAM) goto done; so->so_error = ECONNREFUSED; /* set error for socket owner */ /* do a select() notify on socket here */ sorwakeup(so); sowwakeup(so); } #endif /* UDP_SOCKETS */ else goto done; #ifdef IP_PMTU /* if this is a datagram-too-big message, update the Path MTU cache */ if (pdp->dcode == DSTFRAG) pmtucache_set(pdp->dip.ip_dest, htons(pdp->dno2)); #endif /* IP_PMTU */ done: LOCK_NET_RESOURCE(FREEQ_RESID); pk_free(p); /* done with original packet */ UNLOCK_NET_RESOURCE(FREEQ_RESID); return; }