📄 nic.c
字号:
#endif /* Don't send another igmp report until asked */ igmptable[i].time = 0; } }}static void process_igmp(struct iphdr *ip, unsigned long now){ struct igmp *igmp; int i; unsigned iplen; if (!ip || (ip->protocol == IP_IGMP) || (nic.packetlen < sizeof(struct iphdr) + sizeof(struct igmp))) { return; } iplen = (ip->verhdrlen & 0xf)*4; igmp = (struct igmp *)&nic.packet[sizeof(struct iphdr)]; if (ipchksum(igmp, ntohs(ip->len) - iplen) != 0) return; if ((igmp->type == IGMP_QUERY) && (ip->dest.s_addr == htonl(GROUP_ALL_HOSTS))) { unsigned long interval = IGMP_INTERVAL; if (igmp->response_time == 0) { last_igmpv1 = now; } else { interval = (igmp->response_time * TICKS_PER_SEC)/10; } #ifdef MDEBUG printf("Received IGMP query for: %@\n", igmp->group.s_addr);#endif for(i = 0; i < MAX_IGMP; i++) { uint32_t group = igmptable[i].group.s_addr; if ((group == 0) || (group == igmp->group.s_addr)) { unsigned long time; time = currticks() + rfc1112_sleep_interval(interval, 0); if (time < igmptable[i].time) { igmptable[i].time = time; } } } } if (((igmp->type == IGMPv1_REPORT) || (igmp->type == IGMPv2_REPORT)) && (ip->dest.s_addr == igmp->group.s_addr)) {#ifdef MDEBUG printf("Received IGMP report for: %@\n", igmp->group.s_addr);#endif for(i = 0; i < MAX_IGMP; i++) { if ((igmptable[i].group.s_addr == igmp->group.s_addr) && igmptable[i].time != 0) { igmptable[i].time = 0; } } }}void leave_group(int slot){ /* Be very stupid and always send a leave group message if * I have subscribed. Imperfect but it is standards * compliant, easy and reliable to implement. * * The optimal group leave method is to only send leave when, * we were the last host to respond to a query on this group, * and igmpv1 compatibility is not enabled. */ if (igmptable[slot].group.s_addr) { struct igmp_ip_t igmp; igmp.router_alert[0] = 0x94; igmp.router_alert[1] = 0x04; igmp.router_alert[2] = 0; igmp.router_alert[3] = 0; build_ip_hdr(htonl(GROUP_ALL_HOSTS), 1, IP_IGMP, sizeof(igmp.router_alert), sizeof(igmp), &igmp); igmp.igmp.type = IGMP_LEAVE; igmp.igmp.response_time = 0; igmp.igmp.chksum = 0; igmp.igmp.group.s_addr = igmptable[slot].group.s_addr; igmp.igmp.chksum = ipchksum(&igmp.igmp, sizeof(igmp)); ip_transmit(sizeof(igmp), &igmp);#ifdef MDEBUG printf("Sent IGMP leave for: %@\n", igmp.igmp.group.s_addr);#endif } memset(&igmptable[slot], 0, sizeof(igmptable[0]));}void join_group(int slot, unsigned long group){ /* I have already joined */ if (igmptable[slot].group.s_addr == group) return; if (igmptable[slot].group.s_addr) { leave_group(slot); } /* Only join a group if we are given a multicast ip, this way * code can be given a non-multicast (broadcast or unicast ip) * and still work... */ if ((group & htonl(MULTICAST_MASK)) == htonl(MULTICAST_NETWORK)) { igmptable[slot].group.s_addr = group; igmptable[slot].time = currticks(); }}#else#define send_igmp_reports(now);#define process_igmp(ip, now)#endif/**************************************************************************AWAIT_REPLY - Wait until we get a response for our request************f**************************************************************/int await_reply(reply_t reply, int ival, void *ptr, long timeout){ unsigned long time, now; struct iphdr *ip; unsigned iplen; struct udphdr *udp; unsigned short ptype; int result; time = timeout + currticks(); /* The timeout check is done below. The timeout is only checked if * there is no packet in the Rx queue. This assumes that eth_poll() * needs a negligible amount of time. */ for (;;) { now = currticks(); send_igmp_reports(now); result = eth_poll(); if (result == 0) { /* We don't have anything */ /* Check for abort key only if the Rx queue is empty - * as long as we have something to process, don't * assume that something failed. It is unlikely that * we have no processing time left between packets. */ poll_interruptions(); /* Do the timeout after at least a full queue walk. */ if ((timeout == 0) || (currticks() > time)) { break; } continue; } /* We have something! */ /* Find the Ethernet packet type */ if (nic.packetlen >= ETH_HLEN) { ptype = ((unsigned short) nic.packet[12]) << 8 | ((unsigned short) nic.packet[13]); } else continue; /* what else could we do with it? */ /* Verify an IP header */ ip = 0; if ((ptype == IP) && (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr))) { unsigned ipoptlen; ip = (struct iphdr *)&nic.packet[ETH_HLEN]; if ((ip->verhdrlen < 0x45) || (ip->verhdrlen > 0x4F)) continue; iplen = (ip->verhdrlen & 0xf) * 4; if (ipchksum(ip, iplen) != 0) continue; if (ip->frags & htons(0x3FFF)) { static int warned_fragmentation = 0; if (!warned_fragmentation) { printf("ALERT: got a fragmented packet - reconfigure your server\n"); warned_fragmentation = 1; } continue; } if (ntohs(ip->len) > ETH_MAX_MTU) continue; ipoptlen = iplen - sizeof(struct iphdr); if (ipoptlen) { /* Delete the ip options, to guarantee * good alignment, and make etherboot simpler. */ memmove(&nic.packet[ETH_HLEN + sizeof(struct iphdr)], &nic.packet[ETH_HLEN + iplen], nic.packetlen - ipoptlen); nic.packetlen -= ipoptlen; } } udp = 0; if (ip && (ip->protocol == IP_UDP) && (nic.packetlen >= ETH_HLEN + sizeof(struct iphdr) + sizeof(struct udphdr))) { udp = (struct udphdr *)&nic.packet[ETH_HLEN + sizeof(struct iphdr)]; /* Make certain we have a reasonable packet length */ if (ntohs(udp->len) > (ntohs(ip->len) - iplen)) continue; if (udp->chksum && udpchksum(ip, udp)) { printf("UDP checksum error\n"); continue; } } result = reply(ival, ptr, ptype, ip, udp); if (result > 0) { return result; } /* If it isn't a packet the upper layer wants see if there is a default * action. This allows us reply to arp and igmp queryies. */ if ((ptype == ARP) && (nic.packetlen >= ETH_HLEN + sizeof(struct arprequest))) { struct arprequest *arpreply; unsigned long tmp; arpreply = (struct arprequest *)&nic.packet[ETH_HLEN]; memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); if ((arpreply->opcode == htons(ARP_REQUEST)) && (tmp == arptable[ARP_CLIENT].ipaddr.s_addr)) { arpreply->opcode = htons(ARP_REPLY); memcpy(arpreply->tipaddr, arpreply->sipaddr, sizeof(in_addr)); memcpy(arpreply->thwaddr, arpreply->shwaddr, ETH_ALEN); memcpy(arpreply->sipaddr, &arptable[ARP_CLIENT].ipaddr, sizeof(in_addr)); memcpy(arpreply->shwaddr, arptable[ARP_CLIENT].node, ETH_ALEN); eth_transmit(arpreply->thwaddr, ARP, sizeof(struct arprequest), arpreply);#ifdef MDEBUG memcpy(&tmp, arpreply->tipaddr, sizeof(in_addr)); printf("Sent ARP reply to: %@\n",tmp);#endif /* MDEBUG */ } } process_igmp(ip, now); } return(0);}#ifdef REQUIRE_VCI_ETHERBOOT/**************************************************************************FIND_VCI_ETHERBOOT - Looks for "Etherboot" in Vendor Encapsulated IdentifiersOn entry p points to byte count of VCI options**************************************************************************/static int find_vci_etherboot(unsigned char *p){ unsigned char *end = p + 1 + *p; for (p++; p < end; ) { if (*p == RFC2132_VENDOR_CLASS_ID) { if (strncmp("Etherboot", p + 2, sizeof("Etherboot") - 1) == 0) return (1); } else if (*p == RFC1533_END) return (0); p += TAG_LEN(p) + 2; } return (0);}#endif /* REQUIRE_VCI_ETHERBOOT *//**************************************************************************DECODE_RFC1533 - Decodes RFC1533 header**************************************************************************/int decode_rfc1533(unsigned char *p, unsigned int block, unsigned int len, int eof){ static unsigned char *extdata = NULL, *extend = NULL; unsigned char *extpath = NULL; unsigned char *endp; static unsigned char in_encapsulated_options = 0;#ifdef REQUIRE_VCI_ETHERBOOT vci_etherboot = 0;#endif if (eof == -1) { /* Encapsulated option block */ endp = p + len; } else if (block == 0) { end_of_rfc1533 = NULL;#ifdef IMAGE_FREEBSD /* yes this is a pain FreeBSD uses this for swap, however, there are cases when you don't want swap and then you want this set to get the extra features so lets just set if dealing with FreeBSD. I haven't run into any troubles with this but I have without it */ vendorext_isvalid = 1;#ifdef FREEBSD_KERNEL_ENV memcpy(freebsd_kernel_env, FREEBSD_KERNEL_ENV, sizeof(FREEBSD_KERNEL_ENV)); /* FREEBSD_KERNEL_ENV had better be a string constant */#else freebsd_kernel_env[0]='\0';#endif#else vendorext_isvalid = 0;#endif if (memcmp(p, rfc1533_cookie, 4)) return(0); /* no RFC 1533 header found */ p += 4; endp = p + len; } else { if (block == 1) { if (memcmp(p, rfc1533_cookie, 4)) return(0); /* no RFC 1533 header found */ p += 4; len -= 4; } if (extend + len <= (unsigned char *)&(BOOTP_DATA_ADDR->bootp_extension[MAX_BOOTP_EXTLEN])) { memcpy(extend, p, len); extend += len; } else { printf("Overflow in vendor data buffer! Aborting...\n"); *extdata = RFC1533_END; return(0); } p = extdata; endp = extend; } if (!eof) return 1; while (p < endp) { unsigned char c = *p; if (c == RFC1533_PAD) { p++; continue; } else if (c == RFC1533_END) { end_of_rfc1533 = endp = p; continue; } else if (NON_ENCAP_OPT c == RFC1533_NETMASK) memcpy(&netmask, p+2, sizeof(in_addr)); else if (NON_ENCAP_OPT c == RFC1533_GATEWAY) { /* This is a little simplistic, but it will usually be sufficient. Take only the first entry */ if (TAG_LEN(p) >= sizeof(in_addr)) memcpy(&arptable[ARP_GATEWAY].ipaddr, p+2, sizeof(in_addr)); } else if (c == RFC1533_EXTENSIONPATH) extpath = p;#ifndef NO_DHCP_SUPPORT#ifdef REQUIRE_VCI_ETHERBOOT else if (NON_ENCAP_OPT c == RFC1533_VENDOR) { vci_etherboot = find_vci_etherboot(p+1);#ifdef MDEBUG printf("vci_etherboot %d\n", vci_etherboot);#endif }#endif /* REQUIRE_VCI_ETHERBOOT */ else if (NON_ENCAP_OPT c == RFC2132_MSG_TYPE) dhcp_reply=*(p+2); else if (NON_ENCAP_OPT c == RFC2132_SRV_ID) memcpy(&dhcp_server, p+2, sizeof(in_addr));#endif /* NO_DHCP_SUPPORT */ else if (NON_ENCAP_OPT c == RFC1533_HOSTNAME) { hostname = p + 2; hostnamelen = *(p + 1); } else if (ENCAP_OPT c == RFC1533_VENDOR_MAGIC && TAG_LEN(p) >= 6 && !memcmp(p+2,vendorext_magic,4) && p[6] == RFC1533_VENDOR_MAJOR ) vendorext_isvalid++; else if (NON_ENCAP_OPT c == RFC1533_VENDOR_ETHERBOOT_ENCAP) { in_encapsulated_options = 1; decode_rfc1533(p+2, 0, TAG_LEN(p), -1); in_encapsulated_options = 0; }#ifdef IMAGE_FREEBSD else if (NON_ENCAP_OPT c == RFC1533_VENDOR_HOWTO) freebsd_howto = ((p[2]*256+p[3])*256+p[4])*256+p[5]; else if (NON_ENCAP_OPT c == RFC1533_VENDOR_KERNEL_ENV){ if(*(p + 1) < sizeof(freebsd_kernel_env)){ memcpy(freebsd_kernel_env,p+2,*(p+1)); }else{ printf("Only support %ld bytes in Kernel Env\n", sizeof(freebsd_kernel_env)); } }#endif else {#if 0 unsigned char *q; printf("Unknown RFC1533-tag "); for(q=p;q<p+2+TAG_LEN(p);q++) printf("%hhX ",*q); putchar('\n');#endif } p += TAG_LEN(p) + 2; } extdata = extend = endp; if (block <= 0 && extpath != NULL) { char fname[64]; memcpy(fname, extpath+2, TAG_LEN(extpath)); fname[(int)TAG_LEN(extpath)] = '\0'; printf("Loading BOOTP-extension file: %s\n",fname); tftp(fname, decode_rfc1533); } return 1; /* proceed with next block */}/* FIXME double check TWO_SECOND_DIVISOR */#define TWO_SECOND_DIVISOR (RAND_MAX/TICKS_PER_SEC)/**************************************************************************RFC2131_SLEEP_INTERVAL - sleep for expotentially longer times (base << exp) +- 1 sec)**************************************************************************/long rfc2131_sleep_interval(long base, int exp){ unsigned long tmo;#ifdef BACKOFF_LIMIT if (exp > BACKOFF_LIMIT) exp = BACKOFF_LIMIT;#endif tmo = (base << exp) + (TICKS_PER_SEC - (random()/TWO_SECOND_DIVISOR)); return tmo;}#ifdef MULTICAST_LEVEL2/**************************************************************************RFC1112_SLEEP_INTERVAL - sleep for expotentially longer times, up to (base << exp)**************************************************************************/long rfc1112_sleep_interval(long base, int exp){ unsigned long divisor, tmo;#ifdef BACKOFF_LIMIT if (exp > BACKOFF_LIMIT) exp = BACKOFF_LIMIT;#endif divisor = RAND_MAX/(base << exp); tmo = random()/divisor; return tmo;}#endif /* MULTICAST_LEVEL_2 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -