/** * Resolve and fill-in Ethernet address header for outgoing packet. * * If ARP has the Ethernet address in cache, the given packet is * returned, ready to be sent. * * If ARP does not have the Ethernet address in cache the packet is * queued and a ARP request is sent (on a best-effort basis). This * ARP request is returned as a pbuf, which should be sent by the * caller. * * If ARP failed to allocate resources, NULL is returned. * * A returned non-NULL packet should be sent by the caller. * * @param netif The lwIP network interface which the IP packet will be sent on. * @param ipaddr The IP address of the packet destination. * @param pbuf The pbuf(s) containing the IP packet to be sent. * * @return If non-NULL, a packet ready to be sent. */ struct pbuf * etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) { struct eth_addr *dest, *srcaddr, mcastaddr; struct eth_hdr *ethhdr; u8_t i; /* Make room for Ethernet header. */ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) { /* The pbuf_header() call shouldn't fail, and we'll just bail out if it does.. */ DEBUGF(ETHARP_DEBUG | DBG_TRACE | 2, ("etharp_output: could not allocate room for header.\n")); #ifdef LINK_STATS ++lwip_stats.link.lenerr; #endif /* LINK_STATS */ return NULL; } /* obtain source Ethernet address of the given interface */ srcaddr = (struct eth_addr *)netif->hwaddr; /* assume unresolved Ethernet address */ dest = NULL; /* Construct Ethernet header. Start with looking up deciding which MAC address to use as a destination address. Broadcasts and multicasts are special, all other addresses are looked up in the ARP table. */ /* destination IP address is an IP broadcast address? */ if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, &(netif->netmask))) { /* broadcast on Ethernet also */ dest = (struct eth_addr *)ðbroadcast; } /* destination IP address is an IP multicast address? */ else if (ip_addr_ismulticast(ipaddr)) { /* Hash IP multicast address to MAC address. */ mcastaddr.addr[0] = 0x01; mcastaddr.addr[1] = 0x0; mcastaddr.addr[2] = 0x5e; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); /* destination Ethernet address is multicast */ dest = &mcastaddr; } /* destination IP address is an IP unicast address */ else { /* destination IP network address not on local network? */ /* this occurs if the packet is routed to the default gateway on this interface */ if (!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) { /* gateway available? */ if (netif->gw.addr != 0) { /* use the gateway IP address */ ipaddr = &(netif->gw); } /* no gateway available? */ else { /* IP destination address outside local network, but no gateway available */ return NULL; } } /* Ethernet address for IP destination address is in ARP cache? */ for(i = 0; i < ARP_TABLE_SIZE; ++i) { /* match found? */ if (arp_table[i].state == ETHARP_STATE_STABLE && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { dest = &arp_table[i].ethaddr; break; } } /* could not find the destination Ethernet address in ARP cache? */ if (dest == NULL) { /* ARP query for the IP address, submit this IP packet for queueing */ etharp_query(netif, ipaddr, q); /* return nothing */ return NULL; } /* destination Ethernet address resolved from ARP cache */ else { /* fallthrough */ } } /* destination Ethernet address known */ if (dest != NULL) { /* A valid IP->MAC address mapping was found, so we construct the Ethernet header for the outgoing packet. */ ethhdr = q->payload; for(i = 0; i < netif->hwaddr_len; i++) { ethhdr->dest.addr[i] = dest->addr[i]; ethhdr->src.addr[i] = srcaddr->addr[i]; } ethhdr->type = htons(ETHTYPE_IP); /* return the outgoing packet */ return q; } /* never reached; here for safety */ return NULL; }
/** * Binds the connection to a local portnumber and IP address. If the * IP address is not given (i.e., ipaddr == NULL), the IP address of * the outgoing network interface is used instead. * * @param pcb the tcp_pcb to bind (no check is done whether this pcb is * already bound!) * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind * to any local address * @param port the local port to bind to * @return ERR_USE if the port is already in use * ERR_OK if bound */ err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) { struct tcp_pcb *cpcb; LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); if (port == 0) { port = tcp_new_port(); } /* Check if the address already is in use. */ /* Check the listen pcbs. */ for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_isany(&(cpcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } /* Check the connected pcbs. */ for(cpcb = tcp_active_pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_isany(&(cpcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } /* Check the bound, not yet connected pcbs. */ for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_isany(&(cpcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah), * we have to check the pcbs in TIME-WAIT state, also: */ for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } if (!ip_addr_isany(ipaddr)) { pcb->local_ip = *ipaddr; } pcb->local_port = port; TCP_REG(&tcp_bound_pcbs, pcb); LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); return ERR_OK; }
/** * Send data to a specified address using UDP. * The netif used for sending can be specified. * * This function exists mainly for DHCP, to be able to send UDP packets * on a netif that is still down. * * @param pcb UDP PCB used to send the data. * @param p chain of pbuf's to be sent. * @param dst_ip Destination IP address. * @param dst_port Destination UDP port. * @param netif the netif used for sending. * * dst_ip & dst_port are expected to be in the same byte order as in the pcb. * * @return lwIP error code (@see udp_send for possible error codes) * * @see udp_disconnect() udp_send() */ err_t udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *dst_ip, u16_t dst_port, struct netif *netif) { struct udp_hdr *udphdr; struct ip_addr *src_ip; err_t err; struct pbuf *q; /* q will be sent down the stack */ #if IP_SOF_BROADCAST /* broadcast filter? */ if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); return ERR_VAL; } #endif /* IP_SOF_BROADCAST */ /* if the PCB is not yet bound to a port, bind it here */ if (pcb->local_port == 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); if (err != ERR_OK) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); return err; } } /* not enough space to add an UDP header to first pbuf in given p chain? */ if (pbuf_header(p, UDP_HLEN)) { /* allocate header in a separate new pbuf */ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); return ERR_MEM; } /* chain header q in front of given pbuf p */ pbuf_chain(q, p); /* first pbuf q points to header pbuf */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { /* adding space for header within p succeeded */ /* first pbuf q equals given pbuf */ q = p; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); } LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", (q->len >= sizeof(struct udp_hdr))); /* q now represents the packet to be sent */ udphdr = q->payload; udphdr->src = htons(pcb->local_port); udphdr->dest = htons(dst_port); /* in UDP, 0 checksum means 'no checksum' */ udphdr->chksum = 0x0000; /* PCB local address is IP_ANY_ADDR? */ if (ip_addr_isany(&pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = &(netif->ip_addr); } else { /* check if UDP PCB local IP address is correct * this could be an old address if netif->ip_addr has changed */ if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { /* local_ip doesn't match, drop the packet */ if (q != p) { /* free the header pbuf */ pbuf_free(q); q = NULL; /* p is still referenced by the caller, and will live on */ } return ERR_VAL; } /* use UDP PCB local IP address as source address */ src_ip = &(pcb->local_ip); } LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); #if LWIP_UDPLITE /* UDP Lite protocol? */ if (pcb->flags & UDP_FLAGS_UDPLITE) { u16_t chklen, chklen_hdr; LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); /* set UDP message length in UDP header */ chklen_hdr = chklen = pcb->chksum_len_tx; if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { if (chklen != 0) { LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); } /* For UDP-Lite, checksum length of 0 means checksum over the complete packet. (See RFC 3828 chap. 3.1) At least the UDP-Lite header must be covered by the checksum, therefore, if chksum_len has an illegal value, we generate the checksum over the complete packet to be safe. */ chklen_hdr = 0; chklen = q->tot_len; } udphdr->len = htons(chklen_hdr); /* calculate checksum */ #if CHECKSUM_GEN_UDP udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDPLITE, q->tot_len, chklen); /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; #endif /* CHECKSUM_CHECK_UDP */ /* output to IP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); #if LWIP_NETIF_HWADDRHINT netif->addr_hint = &(pcb->addr_hint); #endif /* LWIP_NETIF_HWADDRHINT*/ err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); #if LWIP_NETIF_HWADDRHINT netif->addr_hint = NULL; #endif /* LWIP_NETIF_HWADDRHINT*/ } else #endif /* LWIP_UDPLITE */ { /* UDP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); udphdr->len = htons(q->tot_len); /* calculate checksum */ #if CHECKSUM_GEN_UDP if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { udphdr->chksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); /* chksum zero must become 0xffff, as zero means 'no checksum' */ if (udphdr->chksum == 0x0000) udphdr->chksum = 0xffff; } #endif /* CHECKSUM_CHECK_UDP */ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); /* output to IP */ #if LWIP_NETIF_HWADDRHINT netif->addr_hint = &(pcb->addr_hint); #endif /* LWIP_NETIF_HWADDRHINT*/ err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); #if LWIP_NETIF_HWADDRHINT netif->addr_hint = NULL; #endif /* LWIP_NETIF_HWADDRHINT*/ } /* TODO: must this be increased even if error occured? */ snmp_inc_udpoutdatagrams(); /* did we chain a separate header pbuf earlier? */ if (q != p) { /* free the header pbuf */ pbuf_free(q); q = NULL; /* p is still referenced by the caller, and will live on */ } UDP_STATS_INC(udp.xmit); return err; }
/** * Actually send a TCP segment over IP */ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb) { u16_t len; struct netif *netif; /** @bug Exclude retransmitted segments from this count. */ snmp_inc_tcpoutsegs(); /* The TCP header has already been constructed, but the ackno and wnd fields remain. */ seg->tcphdr->ackno = htonl(pcb->rcv_nxt); /* silly window avoidance */ if (pcb->rcv_wnd < pcb->mss) { seg->tcphdr->wnd = 0; } else { /* advertise our receive window size in this TCP segment */ seg->tcphdr->wnd = htons(pcb->rcv_wnd); } /* If we don't have a local IP address, we get one by calling ip_route(). */ if (ip_addr_isany(&(pcb->local_ip))) { netif = ip_route(&(pcb->remote_ip)); if (netif == NULL) { return; } ip_addr_set(&(pcb->local_ip), &(netif->ip_addr)); } pcb->rtime = 0; if (pcb->rttest == 0) { pcb->rttest = tcp_ticks; pcb->rtseq = ntohl(seg->tcphdr->seqno); LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq)); } LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n", htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) + seg->len)); len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload); seg->p->len -= len; seg->p->tot_len -= len; seg->p->payload = seg->tcphdr; seg->tcphdr->chksum = 0; #if CHECKSUM_GEN_TCP seg->tcphdr->chksum = inet_chksum_pseudo(seg->p, &(pcb->local_ip), &(pcb->remote_ip), IP_PROTO_TCP, seg->p->tot_len); #endif TCP_STATS_INC(tcp.xmit); ip_output(seg->p, &(pcb->local_ip), &(pcb->remote_ip), pcb->ttl, pcb->tos, IP_PROTO_TCP); }
err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t tos, u8_t proto, struct netif *netif) { struct ip_hdr *iphdr; static u16_t ip_id = 0; snmp_inc_ipoutrequests(); if (dest != IP_HDRINCL) { if (pbuf_header(p, IP_HLEN)) { LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n")); IP_STATS_INC(ip.err); snmp_inc_ipoutdiscards(); return ERR_BUF; } iphdr = p->payload; IPH_TTL_SET(iphdr, ttl); IPH_PROTO_SET(iphdr, proto); ip_addr_set(&(iphdr->dest), dest); IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos); IPH_LEN_SET(iphdr, htons(p->tot_len)); IPH_OFFSET_SET(iphdr, htons(IP_DF)); IPH_ID_SET(iphdr, htons(ip_id)); ++ip_id; if (ip_addr_isany(src)) { ip_addr_set(&(iphdr->src), &(netif->ip_addr)); } else { ip_addr_set(&(iphdr->src), src); } IPH_CHKSUM_SET(iphdr, 0); #if CHECKSUM_GEN_IP IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); #endif } else { iphdr = p->payload; dest = &(iphdr->dest); } #if IP_FRAG /* don't fragment if interface has mtu set to 0 [loopif] */ if (netif->mtu && (p->tot_len > netif->mtu)) return ip_frag(p,netif,dest); #endif IP_STATS_INC(ip.xmit); LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); ip_debug_print(p); LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); return netif->output(netif, p, dest); }
/*-----------------------------------------------------------------------------------*/ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb; struct ip_hdr *iphdr; uint16_t src, dest; #ifdef UDP_STATS ++stats.udp.recv; #endif /* UDP_STATS */ iphdr = (struct ip_hdr *)p->payload; pbuf_header(p, -(UDP_HLEN + IPH_HL(iphdr) * 4)); udphdr = (struct udp_hdr *)((uint8_t *)p->payload - UDP_HLEN); DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %d\n", p->tot_len)); src = NTOHS(udphdr->src); dest = NTOHS(udphdr->dest); #if UDP_DEBUG udp_debug_print(udphdr); #endif /* UDP_DEBUG */ /* Demultiplex packet. First, go for a perfect match. */ for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { DEBUGF(UDP_DEBUG, ("udp_input: pcb local port %d (dgram %d)\n", pcb->local_port, ntohs(udphdr->dest))); if(pcb->remote_port == src && pcb->local_port == dest && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { break; } } if(pcb == NULL) { for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { DEBUGF(UDP_DEBUG, ("udp_input: pcb local port %d (dgram %d)\n", pcb->local_port, dest)); if(pcb->local_port == dest && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src))) && (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)))) { break; } } } /* Check checksum if this is a match or if it was directed at us. */ /* if(pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) {*/ if(pcb != NULL) { DEBUGF(UDP_DEBUG, ("udp_input: calculating checksum\n")); pbuf_header(p, UDP_HLEN); #ifdef IPv6 if(iphdr->nexthdr == IP_PROTO_UDPLITE) { #else if(IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { #endif /* IPv4 */ /* Do the UDP Lite checksum */ if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_UDPLITE, ntohs(udphdr->len)) != 0) { DEBUGF(UDP_DEBUG, ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); #ifdef UDP_STATS ++stats.udp.chkerr; ++stats.udp.drop; #endif /* UDP_STATS */ pbuf_free(p); goto end; } } else { if(udphdr->chksum != 0) { if(inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_UDP, p->tot_len) != 0) { DEBUGF(UDP_DEBUG, ("udp_input: UDP datagram discarded due to failing checksum\n")); #ifdef UDP_STATS ++stats.udp.chkerr; ++stats.udp.drop; #endif /* UDP_STATS */ pbuf_free(p); goto end; } } } pbuf_header(p, -UDP_HLEN); if(pcb != NULL) { pcb->recv(pcb->recv_arg, pcb, p, &(iphdr->src), src); } else { DEBUGF(UDP_DEBUG, ("udp_input: not for us.\n")); /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if(!ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) && !ip_addr_ismulticast(&iphdr->dest)) { /* deconvert from host to network byte order */ udphdr->src = htons(udphdr->src); udphdr->dest = htons(udphdr->dest); /* adjust pbuf pointer */ p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PORT); } #ifdef UDP_STATS ++stats.udp.proterr; ++stats.udp.drop; #endif /* UDP_STATS */ pbuf_free(p); } } else { pbuf_free(p); } end: while(0); /* hack to remove compiler warning */ } /*-----------------------------------------------------------------------------------*/ err_t udp_send(struct udp_pcb *pcb, struct pbuf *p) { struct udp_hdr *udphdr; struct ip_addr *src_ip; err_t err; struct pbuf *q; if(pbuf_header(p, UDP_HLEN)) { q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); if(q == NULL) { return ERR_MEM; } pbuf_chain(q, p); p = q; } udphdr = (struct udp_hdr *)p->payload; udphdr->src = htons(pcb->local_port); udphdr->dest = htons(pcb->remote_port); udphdr->chksum = 0x0000; src_ip = &(pcb->local_ip); DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %d\n", p->tot_len)); if(pcb->flags & UDP_FLAGS_UDPLITE) { udphdr->len = htons(pcb->chksum_len); /* calculate checksum */ udphdr->chksum = inet_chksum_pseudo(p, src_ip, &(pcb->remote_ip), IP_PROTO_UDP, pcb->chksum_len); if(udphdr->chksum == 0x0000) { udphdr->chksum = 0xffff; } err = sr_lwip_output(p, &pcb->local_ip, &pcb->remote_ip, IP_PROTO_UDPLITE); } else { udphdr->len = htons(p->tot_len); /* calculate checksum */ if((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { udphdr->chksum = inet_chksum_pseudo(p, src_ip, &pcb->remote_ip, IP_PROTO_UDP, p->tot_len); if(udphdr->chksum == 0x0000) { udphdr->chksum = 0xffff; } } err = sr_lwip_output(p,&pcb->local_ip, &pcb->remote_ip, IP_PROTO_UDP); } #ifdef UDP_STATS ++stats.udp.xmit; #endif /* UDP_STATS */ return err; } /*-----------------------------------------------------------------------------------*/ err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, uint16_t port) { struct udp_pcb *ipcb; ip_addr_set(&pcb->local_ip, ipaddr); pcb->local_port = port; /* Insert UDP PCB into the list of active UDP PCBs. */ for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { if(pcb == ipcb) { /* Already on the list, just return. */ return ERR_OK; } } /* We need to place the PCB on the list. */ pcb->next = udp_pcbs; udp_pcbs = pcb; DEBUGF(UDP_DEBUG, ("udp_bind: bound to port %d\n", port)); return ERR_OK; } /*-----------------------------------------------------------------------------------*/ err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, uint16_t port) { struct udp_pcb *ipcb; ip_addr_set(&pcb->remote_ip, ipaddr); pcb->remote_port = port; /* Insert UDP PCB into the list of active UDP PCBs. */ for(ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { if(pcb == ipcb) { /* Already on the list, just return. */ return ERR_OK; } } /* We need to place the PCB on the list. */ pcb->next = udp_pcbs; udp_pcbs = pcb; return ERR_OK; } /*-----------------------------------------------------------------------------------*/ void udp_recv(struct udp_pcb *pcb, void (* recv)(void *arg, struct udp_pcb *upcb, struct pbuf *p, struct ip_addr *addr, uint16_t port), void *recv_arg) { pcb->recv = recv; pcb->recv_arg = recv_arg; }
/** * Process an incoming UDP datagram. * * Given an incoming UDP datagram (as a chain of pbufs) this function * finds a corresponding UDP PCB and hands over the pbuf to the pcbs * recv function. If no pcb is found or the datagram is incorrect, the * pbuf is freed. * * @param p pbuf to be demultiplexed to a UDP PCB. * @param inp network interface on which the datagram was received. * */ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; struct ip_hdr *iphdr; u16_t src, dest; u8_t local_match; u8_t broadcast; struct ip_addr dest_ipaddr; struct ip_addr src_ipaddr; PERF_START; UDP_STATS_INC(udp.recv); iphdr = p->payload; /* Check minimum length (IP header + UDP header) * and move payload pointer to UDP header */ if (p->tot_len < (IPH_HL(iphdr) + UDP_HLEN) || pbuf_header(p, -(s16_t) (IPH_HL(iphdr)))) { /* drop short packets */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded"NEWLINE, p->tot_len)); UDP_STATS_INC(udp.lenerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } udphdr = (struct udp_hdr *) p->payload; IPH_DEST(iphdr, &dest_ipaddr); IPH_SRC(iphdr, &src_ipaddr); /* is broadcast packet ? */ broadcast = ip_addr_isbroadcast(&dest_ipaddr, inp); LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F""NEWLINE, p->tot_len)); /* convert src and dest ports to host byte order */ src = ntohs(udphdr->src); dest = ntohs(udphdr->dest); udp_debug_print(udphdr); /* print the UDP source and destination */ /* LWIP_DEBUGF(UDP_DEBUG, ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")"NEWLINE, ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src)));*/ #if LWIP_DHCP pcb = NULL; /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ if (dest == DHCP_CLIENT_PORT) { /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ if (src == DHCP_SERVER_PORT) { if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { /* accept the packe if (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! - inp->dhcp->pcb->remote == ANY or iphdr->src */ if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &src_ipaddr))) { pcb = inp->dhcp->pcb; } } } } else #endif /* LWIP_DHCP */ { prev = NULL; local_match = 0; uncon_pcb = NULL; /* Iterate through the UDP pcb list for a matching pcb. * 'Perfect match' pcbs (connected to the remote port & ip address) are * preferred. If no perfect match is found, the first unconnected pcb that * matches the local port and ip address gets the datagram. */ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { local_match = 0; /* print the PCB local and remote address */ /* LWIP_DEBUGF(UDP_DEBUG, ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")"NEWLINE, ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port)); */ /* compare PCB local addr+port to UDP destination addr+port */ if ((pcb->local_port == dest) && ((!broadcast && ip_addr_isany(&pcb->local_ip)) || ip_addr_cmp(&(pcb->local_ip), &(dest_ipaddr)) || #if LWIP_IGMP ip_addr_ismulticast(&(iphdr->dest)) || #endif /* LWIP_IGMP */ #if IP_SOF_BROADCAST_RECV (broadcast && (pcb->so_options & SOF_BROADCAST)))) { #else /* IP_SOF_BROADCAST_RECV */ (broadcast))) { #endif /* IP_SOF_BROADCAST_RECV */ local_match = 1; if ((uncon_pcb == NULL) && ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { /* the first unconnected matching PCB */ uncon_pcb = pcb; } } /* compare PCB remote addr+port to UDP source addr+port */ if ((local_match != 0) && (pcb->remote_port == src) && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&(pcb->remote_ip), &(src_ipaddr)))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = udp_pcbs; udp_pcbs = pcb; } else { UDP_STATS_INC(udp.cachehit); } break; } prev = pcb; } /* no fully matching pcb found? then look for an unconnected pcb */ if (pcb == NULL) { pcb = uncon_pcb; } } u8_t cmp_addr; if (iphdr->v == IPV4) { cmp_addr = ip4_addr_cmp(&dest_ipaddr.addr.ip4addr, &inp->ip4_addr); } else { cmp_addr = ip6_addr_cmp(&dest_ipaddr.addr.ip6addr, &inp->ip6_addr); } /* Check checksum if this is a match or if it was directed at us. */ if (pcb != NULL || cmp_addr) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum"NEWLINE)); #if LWIP_UDPLITE if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ #if CHECKSUM_CHECK_UDP u16_t chklen = ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { /* For UDP-Lite, checksum length of 0 means checksum over the complete packet (See RFC 3828 chap. 3.1) */ chklen = p->tot_len; } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ UDP_STATS_INC(udp.chkerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } } if (inet_chksum_pseudo_partial(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_input: UDP Lite datagram discarded due to failing checksum"NEWLINE)); UDP_STATS_INC(udp.chkerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } #endif /* CHECKSUM_CHECK_UDP */ } else #endif /* LWIP_UDPLITE */ { #if CHECKSUM_CHECK_UDP if (udphdr->chksum != 0) { if (inet_chksum_pseudo(p, &src_ipaddr.addr, &dest_ipaddr.addr, src_ipaddr.version, IP4_PROTO_UDP, p->tot_len) != 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_input: UDP datagram discarded due to failing checksum"NEWLINE)); UDP_STATS_INC(udp.chkerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } } #endif /* CHECKSUM_CHECK_UDP */ } if (pbuf_header(p, -UDP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed"NEWLINE, 0);UDP_STATS_INC(udp.drop);snmp_inc_udpinerrors(); pbuf_free(p); goto end; } if (pcb != NULL) { snmp_inc_udpindatagrams(); #if SO_REUSE && SO_REUSE_RXTOALL if ((broadcast || ip_addr_ismulticast(¤t_iphdr_dest)) && ip_get_option(pcb, SOF_REUSEADDR)) { /* pass broadcast- or multicast packets to all multicast pcbs if SOF_REUSEADDR is set on the first match */ struct udp_pcb *mpcb; u8_t p_header_changed = 0; for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { if (mpcb != pcb) { /* compare PCB local addr+port to UDP destination addr+port */ if ((mpcb->local_port == dest) && ((!broadcast && ip_addr_isany(&mpcb->local_ip)) || ip_addr_cmp(&(mpcb->local_ip), ¤t_iphdr_dest) || #if LWIP_IGMP ip_addr_ismulticast(¤t_iphdr_dest) || #endif /* LWIP_IGMP */ #if IP_SOF_BROADCAST_RECV (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))) { #else /* IP_SOF_BROADCAST_RECV */ (broadcast))) { #endif /* IP_SOF_BROADCAST_RECV */ /* pass a copy of the packet to all local matches */ if (mpcb->recv != NULL) { struct pbuf *q; /* for that, move payload to IP header again */ if (p_header_changed == 0) { pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); p_header_changed = 1; } q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (q != NULL) { err_t err = pbuf_copy(q, p); if (err == ERR_OK) { /* move payload to UDP data */ pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); } } } } } } if (p_header_changed) { /* and move payload to UDP data again */ pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); } } #endif /* SO_REUSE && SO_REUSE_RXTOALL */ /* callback */ if (pcb->recv != NULL) { /* now the recv function is responsible for freeing p */ pcb->recv(pcb->recv_arg, pcb, p, &src_ipaddr, src); } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); goto end; } } else {
/** * Connects to another host. The function given as the "connected" * argument will be called when the connection has been established. * * @param pcb the tcp_pcb used to establish the connection * @param ipaddr the remote ip address to connect to * @param port the remote tcp port to connect to * @param connected callback function to call when connected (or on error) * @return ERR_VAL if invalid arguments are given * ERR_OK if connect request has been sent * other err_t values if connect request couldn't be sent */ err_t tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port, tcp_connected_fn connected) { err_t ret; u32_t iss; u16_t old_local_port; LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port)); if (ipaddr != NULL) { pcb->remote_ip = *ipaddr; } else { return ERR_VAL; } pcb->remote_port = port; /* check if we have a route to the remote host */ if (ip_addr_isany(&(pcb->local_ip))) { /* no local IP address set, yet. */ struct netif *netif = ip_route(&(pcb->remote_ip)); if (netif == NULL) { /* Don't even try to send a SYN packet if we have no route since that will fail. */ return ERR_RTE; } /* Use the netif's IP address as local address. */ ip_addr_copy(pcb->local_ip, netif->ip_addr); } old_local_port = pcb->local_port; if (pcb->local_port == 0) { pcb->local_port = tcp_new_port(); } #if SO_REUSE if ((pcb->so_options & SOF_REUSEADDR) != 0) { /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure now that the 5-tuple is unique. */ struct tcp_pcb *cpcb; int i; /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */ for (i = 2; i < NUM_TCP_PCB_LISTS; i++) { for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) { if ((cpcb->local_port == pcb->local_port) && (cpcb->remote_port == port) && ip_addr_cmp(&cpcb->local_ip, &pcb->local_ip) && ip_addr_cmp(&cpcb->remote_ip, ipaddr)) { /* linux returns EISCONN here, but ERR_USE should be OK for us */ return ERR_USE; } } } } #endif /* SO_REUSE */ iss = tcp_next_iss(); pcb->rcv_nxt = 0; pcb->snd_nxt = iss; pcb->lastack = iss - 1; pcb->snd_lbb = iss - 1; pcb->rcv_wnd = TCP_WND; pcb->rcv_ann_wnd = TCP_WND; pcb->rcv_ann_right_edge = pcb->rcv_nxt; pcb->snd_wnd = TCP_WND; /* As initial send MSS, we use TCP_MSS but limit it to 536. The send MSS is updated when an MSS option is received. */ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS; #if TCP_CALCULATE_EFF_SEND_MSS pcb->mss = tcp_eff_send_mss(pcb->mss, ipaddr); #endif /* TCP_CALCULATE_EFF_SEND_MSS */ pcb->cwnd = 1; pcb->ssthresh = pcb->mss * 10; #if LWIP_CALLBACK_API pcb->connected = connected; #else /* LWIP_CALLBACK_API */ LWIP_UNUSED_ARG(connected); #endif /* LWIP_CALLBACK_API */ /* Send a SYN together with the MSS option. */ ret = tcp_enqueue_flags(pcb, TCP_SYN); if (ret == ERR_OK) { /* SYN segment was enqueued, changed the pcbs state now */ pcb->state = SYN_SENT; if (old_local_port != 0) { TCP_RMV(&tcp_bound_pcbs, pcb); } TCP_REG(&tcp_active_pcbs, pcb); snmp_inc_tcpactiveopens(); tcp_output(pcb); } return ret; }
/** * The initial input processing of TCP. It verifies the TCP header, demultiplexes * the segment between the PCBs and passes it on to tcp_process(), which implements * the TCP finite state machine. This function is called by the IP layer (in * ip_input()). * * @param p received TCP segment to process (p->payload pointing to the IP header) * @param inp network interface on which this segment was received */ void tcp_input(struct pbuf *p, struct netif *inp) { struct tcp_pcb *pcb, *prev; struct tcp_pcb_listen *lpcb; u8_t hdrlen; err_t err; PERF_START; TCP_STATS_INC(tcp.recv); snmp_inc_tcpinsegs(); iphdr = p->payload; tcphdr = (struct tcp_hdr *)((u8_t *)p->payload + IPH_HL(iphdr) * 4); #if TCP_INPUT_DEBUG tcp_debug_print(tcphdr); #endif /* remove header from payload */ if (pbuf_header(p, -((s16_t)(IPH_HL(iphdr) * 4))) || (p->tot_len < sizeof(struct tcp_hdr))) { /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len)); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } /* Don't even process incoming broadcasts/multicasts. */ if (ip_addr_isbroadcast(&(iphdr->dest), inp) || ip_addr_ismulticast(&(iphdr->dest))) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } #if CHECKSUM_CHECK_TCP /* Verify TCP checksum. */ if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len) != 0) { LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n", inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_TCP, p->tot_len))); #if TCP_DEBUG tcp_debug_print(tcphdr); #endif /* TCP_DEBUG */ TCP_STATS_INC(tcp.chkerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } #endif /* Move the payload pointer in the pbuf so that it points to the TCP data instead of the TCP header. */ hdrlen = TCPH_HDRLEN(tcphdr); if(pbuf_header(p, -(hdrlen * 4))){ /* drop short packets */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n")); TCP_STATS_INC(tcp.lenerr); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } /* Convert fields in TCP header to host byte order. */ tcphdr->src = ntohs(tcphdr->src); tcphdr->dest = ntohs(tcphdr->dest); seqno = tcphdr->seqno = ntohl(tcphdr->seqno); ackno = tcphdr->ackno = ntohl(tcphdr->ackno); tcphdr->wnd = ntohs(tcphdr->wnd); flags = TCPH_FLAGS(tcphdr) & TCP_FLAGS; tcplen = p->tot_len + ((flags & TCP_FIN || flags & TCP_SYN)? 1: 0); /* Demultiplex an incoming segment. First, we check if it is destined for an active connection. */ prev = NULL; for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED); LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT); LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb); if (prev != NULL) { prev->next = pcb->next; pcb->next = tcp_active_pcbs; tcp_active_pcbs = pcb; } LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb); break; } prev = pcb; } if (pcb == NULL) { /* If it did not go to an active connection, we check the connections in the TIME-WAIT state. */ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) { LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT); if (pcb->remote_port == tcphdr->src && pcb->local_port == tcphdr->dest && ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)) && ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest))) { /* We don't really care enough to move this PCB to the front of the list since we are not very likely to receive that many segments for connections in TIME-WAIT. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n")); tcp_timewait_input(pcb); pbuf_free(p); return; } } /* Finally, if we still did not get a match, we check all PCBs that are LISTENing for incoming connections. */ prev = NULL; for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) { if ((ip_addr_isany(&(lpcb->local_ip)) || ip_addr_cmp(&(lpcb->local_ip), &(iphdr->dest))) && lpcb->local_port == tcphdr->dest) { /* Move this PCB to the front of the list so that subsequent lookups will be faster (we exploit locality in TCP segment arrivals). */ if (prev != NULL) { ((struct tcp_pcb_listen *)prev)->next = lpcb->next; /* our successor is the remainder of the listening list */ lpcb->next = tcp_listen_pcbs.listen_pcbs; /* put this listening pcb at the head of the listening list */ tcp_listen_pcbs.listen_pcbs = lpcb; } LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n")); tcp_listen_input(lpcb); pbuf_free(p); return; } prev = (struct tcp_pcb *)lpcb; } } #if TCP_INPUT_DEBUG LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags ")); tcp_debug_print_flags(TCPH_FLAGS(tcphdr)); LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n")); #endif /* TCP_INPUT_DEBUG */ if (pcb != NULL) { /* The incoming segment belongs to a connection. */ #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ /* Set up a tcp_seg structure. */ inseg.next = NULL; inseg.len = p->tot_len; inseg.dataptr = p->payload; inseg.p = p; inseg.tcphdr = tcphdr; recv_data = NULL; recv_flags = 0; /* If there is data which was previously "refused" by upper layer */ if (pcb->refused_data != NULL) { /* Notify again application with data previously received. */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n")); TCP_EVENT_RECV(pcb, pcb->refused_data, ERR_OK, err); if (err == ERR_OK) { pcb->refused_data = NULL; } else { /* drop incoming packets, because pcb is "full" */ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n")); TCP_STATS_INC(tcp.drop); snmp_inc_tcpinerrs(); pbuf_free(p); return; } } tcp_input_pcb = pcb; err = tcp_process(pcb); tcp_input_pcb = NULL; /* A return value of ERR_ABRT means that tcp_abort() was called and that the pcb has been freed. If so, we don't do anything. */ if (err != ERR_ABRT) { if (recv_flags & TF_RESET) { /* TF_RESET means that the connection was reset by the other end. We then call the error callback to inform the application that the connection is dead before we deallocate the PCB. */ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST); tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else if (recv_flags & TF_CLOSED) { /* The connection has been closed and we will deallocate the PCB. */ tcp_pcb_remove(&tcp_active_pcbs, pcb); memp_free(MEMP_TCP_PCB, pcb); } else { err = ERR_OK; /* If the application has registered a "sent" function to be called when new send buffer space is available, we call it now. */ if (pcb->acked > 0) { TCP_EVENT_SENT(pcb, pcb->acked, err); } if (recv_data != NULL) { if(flags & TCP_PSH) { recv_data->flags |= PBUF_FLAG_PUSH; } /* Notify application that data has been received. */ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err); /* If the upper layer can't receive this data, store it */ if (err != ERR_OK) { pcb->refused_data = recv_data; LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n")); } } /* If a FIN segment was received, we call the callback function with a NULL buffer to indicate EOF. */ if (recv_flags & TF_GOT_FIN) { TCP_EVENT_RECV(pcb, NULL, ERR_OK, err); } /* If there were no errors, we try to send something out. */ if (err == ERR_OK) { tcp_output(pcb); } } } /* give up our reference to inseg.p */ if (inseg.p != NULL) { pbuf_free(inseg.p); inseg.p = NULL; } #if TCP_INPUT_DEBUG #if TCP_DEBUG tcp_debug_print_state(pcb->state); #endif /* TCP_DEBUG */ #endif /* TCP_INPUT_DEBUG */ } else { /* If no matching PCB was found, send a TCP RST (reset) to the sender. */ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n")); if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) { TCP_STATS_INC(tcp.proterr); TCP_STATS_INC(tcp.drop); tcp_rst(ackno, seqno + tcplen, &(iphdr->dest), &(iphdr->src), tcphdr->dest, tcphdr->src); } pbuf_free(p); } LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane()); PERF_STOP("tcp_input"); }
/*-----------------------------------------------------------------------------------*/ struct pbuf * etharp_output(struct netif *netif, struct ip_addr *ipaddr, struct pbuf *q) { struct eth_addr *dest, *srcaddr, mcastaddr; struct eth_hdr *ethhdr; struct etharp_hdr *hdr; struct pbuf *p; u8_t i; srcaddr = (struct eth_addr *)netif->hwaddr; /* Make room for Ethernet header. */ if(pbuf_header(q, sizeof(struct eth_hdr)) != 0) { /* The pbuf_header() call shouldn't fail, and we'll just bail out if it does.. */ DEBUGF(ETHARP_DEBUG, ("etharp_output: could not allocate room for header.\n")); #ifdef LINK_STATS ++stats.link.lenerr; #endif /* LINK_STATS */ return NULL; } dest = NULL; /* Construct Ethernet header. Start with looking up deciding which MAC address to use as a destination address. Broadcasts and multicasts are special, all other addresses are looked up in the ARP table. */ if(ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, &(netif->netmask))) { dest = (struct eth_addr *)ðbroadcast; } else if(ip_addr_ismulticast(ipaddr)) { /* Hash IP multicast address to MAC address. */ mcastaddr.addr[0] = 0x01; mcastaddr.addr[1] = 0x0; mcastaddr.addr[2] = 0x5e; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); dest = &mcastaddr; } else { #ifdef __PAULOS__ /* abort on insane conditions */ if (!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) return NULL; if (ipaddr->addr == netif->ip_addr.addr) return NULL; #else if(!ip_addr_maskcmp(ipaddr, &(netif->ip_addr), &(netif->netmask))) { /* Use the IP address of the default gateway if the destination is NOT on the same subnet as we are. ("NOT" added 20021113 psheer@) */ ipaddr = &(netif->gw); } #endif /* We try to find a stable mapping. */ for(i = 0; i < arp_table_last; ++i) { if((arp_table[i].state == ETHARP_STATE_STABLE || arp_table[i].state == ETHARP_STATE_STATIC) && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { dest = &arp_table[i].ethaddr; #if 0 // FIXME: remove this test code if (!((int) rand() % 2)) { dest = NULL; arp_table[i].state = ETHARP_STATE_EMPTY; if (arp_table[i].p) pbuf_free (arp_table[i].p); arp_table[i].p = NULL; arp_table[i].payload = NULL; arp_table[i].len = arp_table[0].tot_len = 0; } #endif break; } } } if(dest == NULL) { /* No destination address has been found, so we'll have to send out an ARP request for the IP address. The outgoing packet is queued unless the queue is full. */ /* We check if we are already querying for this address. If so, we'll bail out. */ for(i = 0; i < arp_table_last; ++i) { if(arp_table[i].state == ETHARP_STATE_PENDING && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) { DEBUGF(ETHARP_DEBUG, ("etharp_output: already queued\n")); return NULL; } } hdr = q->payload; for(i = 0; i < 6; ++i) hdr->ethhdr.src.addr[i] = srcaddr->addr[i]; hdr->ethhdr.type = htons(ETHTYPE_IP); i = etharp_new_entry(q, ipaddr, NULL, ETHARP_STATE_PENDING); /* We allocate a pbuf for the outgoing ARP request packet. */ p = pbuf_alloc(PBUF_RAW, sizeof(struct etharp_hdr) + 2, PBUF_RAM); if(p == NULL) { /* No ARP request packet could be allocated, so we forget about the ARP table entry. */ if(i != ARP_TABLE_SIZE) { arp_table[i].state = ETHARP_STATE_EMPTY; /* We decrease the reference count of the queued pbuf (which now is dequeued). */ DEBUGF(ETHARP_DEBUG, ("etharp_output: couldn't alloc pbuf for query, dequeueing %p\n", q)); } return NULL; } pbuf_header (p, (s16_t) -2); hdr = p->payload; hdr->opcode = htons(ARP_REQUEST); for(i = 0; i < 6; ++i) { hdr->dhwaddr.addr[i] = 0x00; hdr->shwaddr.addr[i] = srcaddr->addr[i]; } memcpy (&(hdr->dipaddr), ipaddr, sizeof (hdr->dipaddr)); memcpy (&(hdr->sipaddr), &(netif->ip_addr), sizeof (hdr->sipaddr)); hdr->hwtype = htons(HWTYPE_ETHERNET); ARPH_HWLEN_SET(hdr, 6); hdr->proto = htons(ETHTYPE_IP); ARPH_PROTOLEN_SET(hdr, sizeof(struct ip_addr)); for(i = 0; i < 6; ++i) { hdr->ethhdr.dest.addr[i] = 0xff; hdr->ethhdr.src.addr[i] = srcaddr->addr[i]; } hdr->ethhdr.type = htons(ETHTYPE_ARP); return p; /* (1) */ } else { /* A valid IP->MAC address mapping was found, so we construct the Ethernet header for the outgoing packet. */ ethhdr = q->payload; for(i = 0; i < 6; i++) { ethhdr->dest.addr[i] = dest->addr[i]; ethhdr->src.addr[i] = srcaddr->addr[i]; } ethhdr->type = htons(ETHTYPE_IP); pbuf_ref (q); /* <--- this is important, because the reference must parallel that when returning over here (1). Callers must then ALWAYS do a pbuf_free on the return value of etharp_output(). */ return q; } }
/* 绑定TCP控制块到本地端口 */ err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) { struct tcp_pcb *cpcb; LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN); /* 未指定端口号,则自动分配 */ if (port == 0) { port = tcp_new_port(); } /* Check if the address already is in use. */ /* Check the listen pcbs. */ /* 检查指定的 IP:端口号 有没有被其它TCP控制块占用 */ /* 先检查处于LISTEN状态的TCP控制块 */ for(cpcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; cpcb != NULL; cpcb = cpcb->next) { /* 端口号相同 */ if (cpcb->local_port == port) { /* 其它TCP控制块绑定到本地所有IP * 或要绑定的TCP控制块绑定到本地所有IP * 或要绑定的IP和其它TCP控制块的本地IP相同 */ if (ip_addr_isany(&(cpcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { /* 该本地IP:本地PORT已经被占用,返回错误 */ return ERR_USE; } } } /* Check the connected pcbs. */ /* 检查处于连接状态TCP控制块 */ for(cpcb = tcp_active_pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_isany(&(cpcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } /* Check the bound, not yet connected pcbs. */ /* 检查已绑定但未连接的TCP控制块 */ for(cpcb = tcp_bound_pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_isany(&(cpcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } /* @todo: until SO_REUSEADDR is implemented (see task #6995 on savannah), * we have to check the pcbs in TIME-WAIT state, also: */ /* 检查处于TIME-WAIT状态的TCP控制块 */ for(cpcb = tcp_tw_pcbs; cpcb != NULL; cpcb = cpcb->next) { if (cpcb->local_port == port) { if (ip_addr_cmp(&(cpcb->local_ip), ipaddr)) { return ERR_USE; } } } /* 设置TCP控制块中的本地IP */ if (!ip_addr_isany(ipaddr)) { pcb->local_ip = *ipaddr; } /* 设置TCP控制块的本地端口号 */ pcb->local_port = port; TCP_REG(&tcp_bound_pcbs, pcb); LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port)); return ERR_OK; }
/** * Process an incoming UDP datagram. * * Given an incoming UDP datagram (as a chain of pbufs) this function * finds a corresponding UDP PCB and hands over the pbuf to the pcbs * recv function. If no pcb is found or the datagram is incorrect, the * pbuf is freed. * * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header) * @param inp network interface on which the datagram was received. * */ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; u16_t src, dest; u8_t broadcast; u8_t for_us = 0; LWIP_UNUSED_ARG(inp); PERF_START; UDP_STATS_INC(udp.recv); /* Check minimum length (UDP header) */ if (p->len < UDP_HLEN) { /* drop short packets */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); UDP_STATS_INC(udp.lenerr); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpinerrors); pbuf_free(p); goto end; } udphdr = (struct udp_hdr *)p->payload; /* is broadcast packet ? */ broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif()); LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); /* convert src and dest ports to host byte order */ src = ntohs(udphdr->src); dest = ntohs(udphdr->dest); udp_debug_print(udphdr); /* print the UDP source and destination */ LWIP_DEBUGF(UDP_DEBUG, ("udp (")); ip_addr_debug_print(UDP_DEBUG, ip_current_dest_addr()); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest))); ip_addr_debug_print(UDP_DEBUG, ip_current_src_addr()); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src))); pcb = NULL; prev = NULL; uncon_pcb = NULL; /* Iterate through the UDP pcb list for a matching pcb. * 'Perfect match' pcbs (connected to the remote port & ip address) are * preferred. If no perfect match is found, the first unconnected pcb that * matches the local port and ip address gets the datagram. */ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { /* print the PCB local and remote address */ LWIP_DEBUGF(UDP_DEBUG, ("pcb (")); ip_addr_debug_print(UDP_DEBUG, &pcb->local_ip); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port)); ip_addr_debug_print(UDP_DEBUG, &pcb->remote_ip); LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port)); /* compare PCB local addr+port to UDP destination addr+port */ if ((pcb->local_port == dest) && (udp_input_local_match(pcb, inp, broadcast) != 0)) { if (((pcb->flags & UDP_FLAGS_CONNECTED) == 0) && ((uncon_pcb == NULL) #if SO_REUSE /* prefer specific IPs over cath-all */ || !ip_addr_isany(&pcb->local_ip) #endif /* SO_REUSE */ )) { /* the first unconnected matching PCB */ uncon_pcb = pcb; } /* compare PCB remote addr+port to UDP source addr+port */ if ((pcb->remote_port == src) && (ip_addr_isany_val(pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = udp_pcbs; udp_pcbs = pcb; } else { UDP_STATS_INC(udp.cachehit); } break; } } prev = pcb; } /* no fully matching pcb found? then look for an unconnected pcb */ if (pcb == NULL) { pcb = uncon_pcb; } /* Check checksum if this is a match or if it was directed at us. */ if (pcb != NULL) { for_us = 1; } else { #if LWIP_IPV6 if (ip_current_is_v6()) { for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0; } #endif /* LWIP_IPV6 */ #if LWIP_IPV4 if (!ip_current_is_v6()) { for_us = ip4_addr_cmp(netif_ip4_addr(inp), ip4_current_dest_addr()); } #endif /* LWIP_IPV4 */ } if (for_us) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n")); #if CHECKSUM_CHECK_UDP IF__NETIF_CHECKSUM_ENABLED(inp, CHECKSUM_CHECK_UDP) { #if LWIP_UDPLITE if (ip_current_header_proto() == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ u16_t chklen = ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { /* For UDP-Lite, checksum length of 0 means checksum over the complete packet (See RFC 3828 chap. 3.1) */ chklen = p->tot_len; } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ goto chkerr; } } if (ip_chksum_pseudo_partial(p, IP_PROTO_UDPLITE, p->tot_len, chklen, ip_current_src_addr(), ip_current_dest_addr()) != 0) { goto chkerr; } } else #endif /* LWIP_UDPLITE */ { if (udphdr->chksum != 0) { if (ip_chksum_pseudo(p, IP_PROTO_UDP, p->tot_len, ip_current_src_addr(), ip_current_dest_addr()) != 0) { goto chkerr; } } } } #endif /* CHECKSUM_CHECK_UDP */ if (pbuf_header(p, -UDP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed\n", 0); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpinerrors); pbuf_free(p); goto end; } if (pcb != NULL) { MIB2_STATS_INC(mib2.udpindatagrams); #if SO_REUSE && SO_REUSE_RXTOALL if (ip_get_option(pcb, SOF_REUSEADDR) && (broadcast || ip_addr_ismulticast(ip_current_dest_addr()))) { /* pass broadcast- or multicast packets to all multicast pcbs if SOF_REUSEADDR is set on the first match */ struct udp_pcb *mpcb; u8_t p_header_changed = 0; s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN); for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { if (mpcb != pcb) { /* compare PCB local addr+port to UDP destination addr+port */ if ((mpcb->local_port == dest) && (udp_input_local_match(mpcb, inp, broadcast) != 0)) { /* pass a copy of the packet to all local matches */ if (mpcb->recv != NULL) { struct pbuf *q; /* for that, move payload to IP header again */ if (p_header_changed == 0) { pbuf_header_force(p, hdrs_len); p_header_changed = 1; } q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); if (q != NULL) { err_t err = pbuf_copy(q, p); if (err == ERR_OK) { /* move payload to UDP data */ pbuf_header(q, -hdrs_len); mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); } } } } } } if (p_header_changed) { /* and move payload to UDP data again */ pbuf_header(p, -hdrs_len); } } #endif /* SO_REUSE && SO_REUSE_RXTOALL */ /* callback */ if (pcb->recv != NULL) { /* now the recv function is responsible for freeing p */ pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); goto end; } } else { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); #if LWIP_ICMP || LWIP_ICMP6 /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) { /* move payload pointer back to ip header */ pbuf_header_force(p, ip_current_header_tot_len() + UDP_HLEN); icmp_port_unreach(ip_current_is_v6(), p); } #endif /* LWIP_ICMP || LWIP_ICMP6 */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); MIB2_STATS_INC(mib2.udpnoports); pbuf_free(p); } } else {
/** * This function is called by the network interface device driver when * an IP packet is received. The function does the basic checks of the * IP header such as packet size being at least larger than the header * size etc. If the packet was not destined for us, the packet is * forwarded (using ip_forward). The IP checksum is always checked. * * Finally, the packet is sent to the upper layer protocol input function. * * @param p the received IP packet (p->payload points to IP header) * @param inp the netif on which this packet was received * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't * processed, but currently always returns ERR_OK) */ err_t ip_input(struct pbuf *p, struct netif *inp) { //static int mytmptest; struct ip_hdr *iphdr; struct netif *netif; u16_t iphdr_hlen; u16_t iphdr_len; #if LWIP_DHCP int check_ip_src=1; #endif /* LWIP_DHCP */ IP_STATS_INC(ip.recv); snmp_inc_ipinreceives(); /* identify the IP header */ iphdr = p->payload; if (IPH_V(iphdr) != 4) { LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.err); IP_STATS_INC(ip.drop); snmp_inc_ipinhdrerrors(); return ERR_OK; } /* obtain IP header length in number of 32-bit words */ iphdr_hlen = IPH_HL(iphdr); /* calculate IP header length in bytes */ iphdr_hlen *= 4; /* obtain ip length in bytes */ iphdr_len = ntohs(IPH_LEN(iphdr)); /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) { if (iphdr_hlen > p->len) LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n", iphdr_hlen, p->len)); if (iphdr_len > p->tot_len) LWIP_DEBUGF(IP_DEBUG | 2, ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), " "IP packet dropped.\n", iphdr_len, p->tot_len)); /* free (drop) packet pbufs */ pbuf_free(p); IP_STATS_INC(ip.lenerr); IP_STATS_INC(ip.drop); snmp_inc_ipindiscards(); return ERR_OK; } /* verify checksum */ #if CHECKSUM_CHECK_IP if (inet_chksum(iphdr, iphdr_hlen) != 0) { LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen))); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.chkerr); IP_STATS_INC(ip.drop); snmp_inc_ipinhdrerrors(); return ERR_OK; } #endif /* Trim pbuf. This should have been done at the netif layer, * but we'll do it anyway just to be sure that its done. */ pbuf_realloc(p, iphdr_len); /* match packet against an interface, i.e. is this packet for us? */ #if LWIP_IGMP if (ip_addr_ismulticast(&(iphdr->dest))) { if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, &(iphdr->dest)))) { netif = inp; } else { netif = NULL; } } else #endif /* LWIP_IGMP */ { /* start trying with inp. if that's not acceptable, start walking the list of configured netifs. 'first' is used as a boolean to mark whether we started walking the list */ int first = 1; netif = inp; do { LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", iphdr->dest.addr, netif->ip_addr.addr, iphdr->dest.addr & netif->netmask.addr, netif->ip_addr.addr & netif->netmask.addr, iphdr->dest.addr & ~(netif->netmask.addr))); /* interface is up and configured? */ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { /* unicast to this interface address? */ if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || /* or broadcast on this interface network address? */ ip_addr_isbroadcast(&(iphdr->dest), netif)) { LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", netif->name[0], netif->name[1])); /* break out of for loop */ break; } } if (first) { first = 0; netif = netif_list; } else { netif = netif->next; } if (netif == inp) { netif = netif->next; } } while(netif != NULL); } #if LWIP_DHCP /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. * According to RFC 1542 section 3.1.1, referred by RFC 2131). */ if (netif == NULL) { /* remote port is DHCP server? */ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest))); if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen))->dest) == DHCP_CLIENT_PORT) { LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n")); netif = inp; check_ip_src = 0; } } } #endif /* LWIP_DHCP */ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */ #if LWIP_DHCP if (check_ip_src) #endif /* LWIP_DHCP */ { if ((ip_addr_isbroadcast(&(iphdr->src), inp)) || (ip_addr_ismulticast(&(iphdr->src)))) { /* packet source is not valid */ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: packet source is not valid.\n")); /* free (drop) packet pbufs */ pbuf_free(p); IP_STATS_INC(ip.drop); snmp_inc_ipinaddrerrors(); snmp_inc_ipindiscards(); return ERR_OK; } } /* packet not for us? */ if (netif == NULL) { /* packet not for us, route or discard */ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | 1, ("ip_input: packet not for us.\n")); #if IP_FORWARD /* non-broadcast packet? */ if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) { /* try to forward IP packet on (other) interfaces */ ip_forward(p, iphdr, inp); } else #endif /* IP_FORWARD */ { snmp_inc_ipinaddrerrors(); snmp_inc_ipindiscards(); } pbuf_free(p); return ERR_OK; } /* packet consists of multiple fragments? */ if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { #if IP_REASSEMBLY /* packet fragment reassembly code present? */ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); /* reassemble the packet*/ p = ip_reass(p); /* packet not fully reassembled yet? */ if (p == NULL) { return ERR_OK; } iphdr = p->payload; #else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ pbuf_free(p); LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", ntohs(IPH_OFFSET(iphdr)))); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); /* unsupported protocol feature */ snmp_inc_ipinunknownprotos(); return ERR_OK; #endif /* IP_REASSEMBLY */ } #if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */ #if LWIP_IGMP /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */ if((iphdr_hlen > IP_HLEN && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) { #else if (iphdr_hlen > IP_HLEN) { #endif /* LWIP_IGMP */ LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n")); pbuf_free(p); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); /* unsupported protocol feature */ snmp_inc_ipinunknownprotos(); return ERR_OK; } #endif /* IP_OPTIONS_ALLOWED == 0 */ /* send to upper layers */ //acoral_print("\n ip_input.=== %d\n", mytmptest++); //acoral_prints("\r\n===ip_input===\r\n"); LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); ip_debug_print(p); LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); #if LWIP_RAW /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) #endif /* LWIP_RAW */ { //acoral_print("\nip proto: %d\n",ntohs((iphdr)->_ttl_proto) & 0xff); switch (IPH_PROTO(iphdr)) { #if LWIP_UDP case IP_PROTO_UDP: #if LWIP_UDPLIT fE case IP_PROTO_UDPLITE: #endif /* LWIP_UDPLITE */ snmp_inc_ipindelivers(); //acoral_prints("\nUDP\n"); udp_input(p, inp); break; #endif /* LWIP_UDP */ #if LWIP_TCP case IP_PROTO_TCP: snmp_inc_ipindelivers(); //acoral_print("\nTCP: tot_len:%d, type:%d\n", p->tot_len, p->type); tcp_input(p, inp); //acoral_print("\ni am here.=== %d\n", mytmptest++); //pbuf_free(p); break; #endif /* LWIP_TCP */ #if LWIP_ICMP case IP_PROTO_ICMP: snmp_inc_ipindelivers(); //acoral_prints("\nICMPS\n"); icmp_input(p, inp); //acoral_prints("\nICMPE\n"); break; #endif /* LWIP_ICMP */ #if LWIP_IGMP case IP_PROTO_IGMP: igmp_input(p,inp,&(iphdr->dest)); break; #endif /* LWIP_IGMP */ default: #if LWIP_ICMP /* send ICMP destination protocol unreachable unless is was a broadcast */ if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && !ip_addr_ismulticast(&(iphdr->dest))) { p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } #endif /* LWIP_ICMP */ pbuf_free(p); LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); IP_STATS_INC(ip.proterr); IP_STATS_INC(ip.drop); snmp_inc_ipinunknownprotos(); } } //acoral_print("\n=== input ok ===\n"); return ERR_OK; } /** * Sends an IP packet on a network interface. This function constructs * the IP header and calculates the IP header checksum. If the source * IP address is NULL, the IP address of the outgoing network * interface is filled in as source address. * If the destination IP address is IP_HDRINCL, p is assumed to already * include an IP header and p->payload points to it instead of the data. * * @param p the packet to send (p->payload points to the data, e.g. next protocol header; if dest == IP_HDRINCL, p already includes an IP header and p->payload points to that IP header) * @param src the source IP address to send from (if src == IP_ADDR_ANY, the * IP address of the netif used to send is used as source address) * @param dest the destination IP address to send the packet to * @param ttl the TTL value to be set in the IP header * @param tos the TOS value to be set in the IP header * @param proto the PROTOCOL to be set in the IP header * @param netif the netif on which to send this packet * @return ERR_OK if the packet was sent OK * ERR_BUF if p doesn't have enough space for IP/LINK headers * returns errors returned by netif->output * * @note ip_id: RFC791 "some host may be able to simply use * unique identifiers independent of destination" */ err_t ip_output_if(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t tos, u8_t proto, struct netif *netif) { struct ip_hdr *iphdr; static u16_t ip_id = 0; snmp_inc_ipoutrequests(); /* Should the IP header be generated or is it already included in p? */ if (dest != IP_HDRINCL) { /* generate IP header */ if (pbuf_header(p, IP_HLEN)) { LWIP_DEBUGF(IP_DEBUG | 2, ("ip_output: not enough room for IP header in pbuf\n")); IP_STATS_INC(ip.err); snmp_inc_ipoutdiscards(); return ERR_BUF; } iphdr = p->payload; LWIP_ASSERT("check that first pbuf can hold struct ip_hdr", (p->len >= sizeof(struct ip_hdr))); IPH_TTL_SET(iphdr, ttl); IPH_PROTO_SET(iphdr, proto); ip_addr_set(&(iphdr->dest), dest); IPH_VHLTOS_SET(iphdr, 4, IP_HLEN / 4, tos); IPH_LEN_SET(iphdr, htons(p->tot_len)); IPH_OFFSET_SET(iphdr, 0); IPH_ID_SET(iphdr, htons(ip_id)); ++ip_id; if (ip_addr_isany(src)) { ip_addr_set(&(iphdr->src), &(netif->ip_addr)); } else { ip_addr_set(&(iphdr->src), src); } IPH_CHKSUM_SET(iphdr, 0); #if CHECKSUM_GEN_IP IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN)); #endif } else { /* IP header already included in p */ iphdr = p->payload; dest = &(iphdr->dest); } #if IP_FRAG /* don't fragment if interface has mtu set to 0 [loopif] */ if (netif->mtu && (p->tot_len > netif->mtu)) return ip_frag(p,netif,dest); #endif IP_STATS_INC(ip.xmit); LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num)); ip_debug_print(p); LWIP_DEBUGF(IP_DEBUG, ("netif->output()")); //acoral_print("\r\nIpOutPutIf***netif\r\n"); return netif->output(netif, p, dest); } /** * Simple interface to ip_output_if. It finds the outgoing network * interface and calls upon ip_output_if to do the actual work. * * @param p the packet to send (p->payload points to the data, e.g. next protocol header; if dest == IP_HDRINCL, p already includes an IP header and p->payload points to that IP header) * @param src the source IP address to send from (if src == IP_ADDR_ANY, the * IP address of the netif used to send is used as source address) * @param dest the destination IP address to send the packet to * @param ttl the TTL value to be set in the IP header * @param tos the TOS value to be set in the IP header * @param proto the PROTOCOL to be set in the IP header * * @return ERR_RTE if no route is found * see ip_output_if() for more return values */ err_t ip_output(struct pbuf *p, struct ip_addr *src, struct ip_addr *dest, u8_t ttl, u8_t tos, u8_t proto) { struct netif *netif; if ((netif = ip_route(dest)) == NULL) { return ERR_RTE; } //acoral_print("\r\nIP_OUTPUT\r\n"); return ip_output_if(p, src, dest, ttl, tos, proto, netif); } #if IP_DEBUG /* Print an IP header by using LWIP_DEBUGF * @param p an IP packet, p->payload pointing to the IP header */ void ip_debug_print(struct pbuf *p) { struct ip_hdr *iphdr = p->payload; u8_t *payload; payload = (u8_t *)iphdr + IP_HLEN; LWIP_DEBUGF(IP_DEBUG, ("IP header:\n")); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n", IPH_V(iphdr), IPH_HL(iphdr), IPH_TOS(iphdr), ntohs(IPH_LEN(iphdr)))); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n", ntohs(IPH_ID(iphdr)), ntohs(IPH_OFFSET(iphdr)) >> 15 & 1, ntohs(IPH_OFFSET(iphdr)) >> 14 & 1, ntohs(IPH_OFFSET(iphdr)) >> 13 & 1, ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n", IPH_TTL(iphdr), IPH_PROTO(iphdr), ntohs(IPH_CHKSUM(iphdr)))); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n", ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src))); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n", ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest))); LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n")); }
bool IPAddress::isSet () const { return !ip_addr_isany(&_ip); }
/** * Called from ip_input() if a new IGMP packet is received. * * @param p received igmp packet, p->payload pointing to the ip header * @param inp network interface on which the packet was received * @param dest destination ip address of the igmp packet */ void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest) { struct ip_hdr * iphdr; struct igmp_msg* igmp; struct igmp_group* group; struct igmp_group* groupref; IGMP_STATS_INC(igmp.recv); /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */ iphdr = (struct ip_hdr *)p->payload; if (pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4)) || (p->len < IGMP_MINLEN)) { pbuf_free(p); IGMP_STATS_INC(igmp.lenerr); LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n")); return; } LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from ")); ip_addr_debug_print(IGMP_DEBUG, &(iphdr->src)); LWIP_DEBUGF(IGMP_DEBUG, (" to address ")); ip_addr_debug_print(IGMP_DEBUG, &(iphdr->dest)); LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp)); /* Now calculate and check the checksum */ igmp = (struct igmp_msg *)p->payload; if (inet_chksum(igmp, p->len)) { pbuf_free(p); IGMP_STATS_INC(igmp.chkerr); LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n")); return; } /* Packet is ok so find an existing group */ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */ /* If group can be found or create... */ if (!group) { pbuf_free(p); IGMP_STATS_INC(igmp.drop); LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n")); return; } /* NOW ACT ON THE INCOMING MESSAGE TYPE... */ switch (igmp->igmp_msgtype) { case IGMP_MEMB_QUERY: { /* IGMP_MEMB_QUERY to the "all systems" address ? */ if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) { /* THIS IS THE GENERAL QUERY */ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); if (igmp->igmp_maxresp == 0) { IGMP_STATS_INC(igmp.rx_v1); LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n")); igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR; } else { IGMP_STATS_INC(igmp.rx_general); } groupref = igmp_group_list; while (groupref) { /* Do not send messages on the all systems group address! */ if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) { igmp_delaying_member(groupref, igmp->igmp_maxresp); } groupref = groupref->next; } } else { /* IGMP_MEMB_QUERY to a specific group ? */ if (!ip_addr_isany(&igmp->igmp_group_address)) { LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group ")); ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address); if (ip_addr_cmp(dest, &allsystems)) { ip_addr_t groupaddr; LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); /* we first need to re-look for the group since we used dest last time */ ip_addr_copy(groupaddr, igmp->igmp_group_address); group = igmp_lookfor_group(inp, &groupaddr); } else { LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp))); } if (group != NULL) { IGMP_STATS_INC(igmp.rx_group); igmp_delaying_member(group, igmp->igmp_maxresp); } else { IGMP_STATS_INC(igmp.drop); } } else { IGMP_STATS_INC(igmp.proterr); } } break; } case IGMP_V2_MEMB_REPORT: { LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n")); IGMP_STATS_INC(igmp.rx_report); if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) { /* This is on a specific group we have already looked up */ group->timer = 0; /* stopped */ group->group_state = IGMP_GROUP_IDLE_MEMBER; group->last_reporter_flag = 0; } break; } default: { LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n", igmp->igmp_msgtype, group->group_state, &group, group->netif)); IGMP_STATS_INC(igmp.proterr); break; } } pbuf_free(p); return; }
/*-----------------------------------------------------------------------------------*/ err_t ip_input(struct pbuf *p, struct netif *inp) { static struct ip_hdr *iphdr; static struct netif *netif; static u8_t hl; #ifdef IP_STATS ++stats.ip.recv; #endif /* IP_STATS */ /* identify the IP header */ iphdr = p->payload; if(IPH_V(iphdr) != 4) { DEBUGF(IP_DEBUG, ("IP packet dropped due to bad version number %d\n", IPH_V(iphdr))); #if IP_DEBUG ip_debug_print(p); #endif /* IP_DEBUG */ pbuf_free(p); #ifdef IP_STATS ++stats.ip.err; ++stats.ip.drop; #endif /* IP_STATS */ return ERR_OK; } hl = IPH_HL(iphdr); if(hl * 4 > p->len) { DEBUGF(IP_DEBUG, ("IP packet dropped due to too short packet %d\n", p->len)); pbuf_free(p); #ifdef IP_STATS ++stats.ip.lenerr; ++stats.ip.drop; #endif /* IP_STATS */ return ERR_OK; } /* verify checksum */ if(inet_chksum(iphdr, hl * 4) != 0) { DEBUGF(IP_DEBUG, ("IP packet dropped due to failing checksum 0x%x\n", inet_chksum(iphdr, hl * 4))); #if IP_DEBUG ip_debug_print(p); #endif /* IP_DEBUG */ pbuf_free(p); #ifdef IP_STATS ++stats.ip.chkerr; ++stats.ip.drop; #endif /* IP_STATS */ return ERR_OK; } /* Trim pbuf. This should have been done at the netif layer, but we'll do it anyway just to be sure that its done. */ pbuf_realloc(p, ntohs(IPH_LEN(iphdr))); /* is this packet for us? */ for(netif = netif_list; netif != NULL; netif = netif->next) { DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%lx netif->ip_addr 0x%lx (0x%lx, 0x%lx, 0x%lx)\n", iphdr->dest.addr, netif->ip_addr.addr, iphdr->dest.addr & netif->netmask.addr, netif->ip_addr.addr & netif->netmask.addr, iphdr->dest.addr & ~(netif->netmask.addr))); if(ip_addr_isany(&(netif->ip_addr)) || ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || (ip_addr_isbroadcast(&(iphdr->dest), &(netif->netmask)) && ip_addr_maskcmp(&(iphdr->dest), &(netif->ip_addr), &(netif->netmask))) || ip_addr_cmp(&(iphdr->dest), IP_ADDR_BROADCAST)) { break; } } #if LWIP_DHCP /* If a DHCP packet has arrived on the interface, we pass it up the stack regardless of destination IP address. The reason is that DHCP replies are sent to the IP adress that will be given to this node (as recommended by RFC 1542 section 3.1.1, referred by RFC 2131). */ if(IPH_PROTO(iphdr) == IP_PROTO_UDP && ((struct udp_hdr *)((u8_t *)iphdr + IPH_HL(iphdr) * 4/sizeof(u8_t)))->src == DHCP_SERVER_PORT) { netif = inp; } #endif /* LWIP_DHCP */ if(netif == NULL) { /* packet not for us, route or discard */ DEBUGF(IP_DEBUG, ("ip_input: packet not for us.\n")); #if IP_FORWARD if(!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask))) { ip_forward(p, iphdr, inp); } #endif /* IP_FORWARD */ pbuf_free(p); return ERR_OK; } #if IP_REASSEMBLY if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { p = ip_reass(p); if(p == NULL) { return ERR_OK; } iphdr = p->payload; } #else /* IP_REASSEMBLY */ if((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { pbuf_free(p); DEBUGF(IP_DEBUG, ("IP packet dropped since it was fragmented (0x%x).\n", ntohs(IPH_OFFSET(iphdr)))); #ifdef IP_STATS ++stats.ip.opterr; ++stats.ip.drop; #endif /* IP_STATS */ return ERR_OK; } #endif /* IP_REASSEMBLY */ #if IP_OPTIONS == 0 if(hl * 4 > IP_HLEN) { DEBUGF(IP_DEBUG, ("IP packet dropped since there were IP options.\n")); pbuf_free(p); #ifdef IP_STATS ++stats.ip.opterr; ++stats.ip.drop; #endif /* IP_STATS */ return ERR_OK; } #endif /* IP_OPTIONS == 0 */ /* send to upper layers */ #if IP_DEBUG DEBUGF(IP_DEBUG, ("ip_input: \n")); ip_debug_print(p); DEBUGF(IP_DEBUG, ("ip_input: p->len %d p->tot_len %d\n", p->len, p->tot_len)); #endif /* IP_DEBUG */ switch(IPH_PROTO(iphdr)) { #if LWIP_UDP > 0 case IP_PROTO_UDP: udp_input(p, inp); break; #endif /* LWIP_UDP */ #if LWIP_TCP > 0 case IP_PROTO_TCP: tcp_input(p, inp); break; #endif /* LWIP_TCP */ case IP_PROTO_ICMP: icmp_input(p, inp); break; default: /* send ICMP destination protocol unreachable unless is was a broadcast */ if(!ip_addr_isbroadcast(&(iphdr->dest), &(inp->netmask)) && !ip_addr_ismulticast(&(iphdr->dest))) { p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } pbuf_free(p); DEBUGF(IP_DEBUG, ("Unsupported transportation protocol %d\n", IPH_PROTO(iphdr))); #ifdef IP_STATS ++stats.ip.proterr; ++stats.ip.drop; #endif /* IP_STATS */ } return ERR_OK; }
/** * Update (or insert) a IP/MAC address pair in the ARP cache. * * If a pending entry is resolved, any queued packets will be sent * at this point. * * @param netif netif related to this entry (used for NETIF_ADDRHINT) * @param ipaddr IP address of the inserted ARP entry. * @param ethaddr Ethernet address of the inserted ARP entry. * @param flags @see definition of ETHARP_FLAG_* * * @return * - ERR_OK Succesfully updated ARP cache. * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. * * @see pbuf_free() */ static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); /* non-unicast address? */ if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find or create ARP entry */ i = find_entry(ipaddr, flags); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; } #if ETHARP_SUPPORT_STATIC_ENTRIES if (flags & ETHARP_FLAG_STATIC_ENTRY) { /* record static type */ arp_table[i].static_entry = 1; } #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ /* mark it stable */ arp_table[i].state = ETHARP_STATE_STABLE; #if LWIP_SNMP /* record network interface */ arp_table[i].netif = netif; #endif /* LWIP_SNMP */ /* insert in SNMP ARP index tree */ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); /* update address */ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); /* reset time stamp */ arp_table[i].ctime = 0; #if ARP_QUEUEING /* this is where we will send out queued packets! */ while (arp_table[i].q != NULL) { struct pbuf *p; /* remember remainder of queue */ struct etharp_q_entry *q = arp_table[i].q; /* pop first item off the queue */ arp_table[i].q = q->next; /* get the packet pointer */ p = q->p; /* now queue entry can be freed */ memp_free(MEMP_ARP_QUEUE, q); /* send the queued IP packet */ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); /* free the queued IP packet */ pbuf_free(p); } #endif /* ARP_QUEUEING */ return ERR_OK; }
err_t udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb; struct ip_hdr *iphdr; unsigned short src, dest; stats.udp.recv++; iphdr = p->payload; if (pbuf_header(p, -(IPH_HL(iphdr) * 4)) < 0 || p->tot_len < sizeof(struct udp_hdr)) { kprintf(KERN_WARNING "udp_input: short packet (%u bytes) discarded\n", p->tot_len); stats.udp.lenerr++; stats.udp.drop++; return -EPROTO; } udphdr = p->payload; //udp_debug_print(udphdr); #ifdef CHECK_UDP_CHECKSUM // Check checksum if ((inp->flags & NETIF_UDP_RX_CHECKSUM_OFFLOAD) == 0) { if (udphdr->chksum != 0) { if (inet_chksum_pseudo(p, &iphdr->src, &iphdr->dest, IP_PROTO_UDP, p->tot_len) != 0) { kprintf(KERN_WARNING "udp_input: UDP datagram discarded due to failing checksum\n"); stats.udp.chkerr++; stats.udp.drop++; return -ECHKSUM; } } } #endif src = NTOHS(udphdr->src); dest = NTOHS(udphdr->dest); // Demultiplex packet. First, go for a perfect match for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { if (pcb->remote_port == src && pcb->local_port == dest && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, &iphdr->src)) && (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, &iphdr->dest))) { break; } } if (pcb == NULL) { // No fully matching pcb found, look for an unconnected pcb for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { if (!(pcb->flags & UDP_FLAGS_CONNECTED) && pcb->local_port == dest && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&pcb->remote_ip, &iphdr->src)) && (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, &iphdr->dest))) { break; } } } if (pcb == NULL) { // No match was found, send ICMP destination port unreachable unless // destination address was broadcast/multicast. if (!ip_addr_isbroadcast(&iphdr->dest, &inp->netmask) && !ip_addr_ismulticast(&iphdr->dest)) { // Adjust pbuf pointer p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PORT); } ++stats.udp.proterr; ++stats.udp.drop; return -EHOSTUNREACH; } pbuf_header(p, -UDP_HLEN); return pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, src); }
/** * Process an incoming UDP datagram. * * Given an incoming UDP datagram (as a chain of pbufs) this function * finds a corresponding UDP PCB and hands over the pbuf to the pcbs * recv function. If no pcb is found or the datagram is incorrect, the * pbuf is freed. * * @param p pbuf to be demultiplexed to a UDP PCB. * @param inp network interface on which the datagram was received. * */ void udp_input(struct pbuf *p, struct netif *inp) { struct udp_hdr *udphdr; struct udp_pcb *pcb, *prev; struct udp_pcb *uncon_pcb; struct ip_hdr *iphdr; u16_t src, dest; u8_t local_match; u8_t broadcast; PERF_START; UDP_STATS_INC(udp.recv); iphdr = p->payload; /* Check minimum length (IP header + UDP header) * and move payload pointer to UDP header */ if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { /* drop short packets */ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\r\n", p->tot_len)); UDP_STATS_INC(udp.lenerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } udphdr = (struct udp_hdr *)p->payload; /* is broadcast packet ? */ broadcast = ip_addr_isbroadcast(&(iphdr->dest), inp); LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\r\n", p->tot_len)); /* convert src and dest ports to host byte order */ src = ntohs(udphdr->src); dest = ntohs(udphdr->dest); udp_debug_print(udphdr); /* print the UDP source and destination */ LWIP_DEBUGF(UDP_DEBUG, ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\r\n", ip4_addr1(&iphdr->dest), ip4_addr2(&iphdr->dest), ip4_addr3(&iphdr->dest), ip4_addr4(&iphdr->dest), ntohs(udphdr->dest), ip4_addr1(&iphdr->src), ip4_addr2(&iphdr->src), ip4_addr3(&iphdr->src), ip4_addr4(&iphdr->src), ntohs(udphdr->src))); #if LWIP_DHCP pcb = NULL; /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ if (dest == DHCP_CLIENT_PORT) { /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ if (src == DHCP_SERVER_PORT) { if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { /* accept the packe if (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! - inp->dhcp->pcb->remote == ANY or iphdr->src */ if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), &(iphdr->src)))) { pcb = inp->dhcp->pcb; } } } } else #endif /* LWIP_DHCP */ { prev = NULL; local_match = 0; uncon_pcb = NULL; /* Iterate through the UDP pcb list for a matching pcb. * 'Perfect match' pcbs (connected to the remote port & ip address) are * preferred. If no perfect match is found, the first unconnected pcb that * matches the local port and ip address gets the datagram. */ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { local_match = 0; /* print the PCB local and remote address */ LWIP_DEBUGF(UDP_DEBUG, ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\r\n", ip4_addr1(&pcb->local_ip), ip4_addr2(&pcb->local_ip), ip4_addr3(&pcb->local_ip), ip4_addr4(&pcb->local_ip), pcb->local_port, ip4_addr1(&pcb->remote_ip), ip4_addr2(&pcb->remote_ip), ip4_addr3(&pcb->remote_ip), ip4_addr4(&pcb->remote_ip), pcb->remote_port)); /* compare PCB local addr+port to UDP destination addr+port */ if ((pcb->local_port == dest) && ((!broadcast && ip_addr_isany(&pcb->local_ip)) || ip_addr_cmp(&(pcb->local_ip), &(iphdr->dest)) || #if LWIP_IGMP ip_addr_ismulticast(&(iphdr->dest)) || #endif /* LWIP_IGMP */ #if IP_SOF_BROADCAST_RECV (broadcast && (pcb->so_options & SOF_BROADCAST)))) { #else /* IP_SOF_BROADCAST_RECV */ (broadcast))) { #endif /* IP_SOF_BROADCAST_RECV */ local_match = 1; if ((uncon_pcb == NULL) && ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { /* the first unconnected matching PCB */ uncon_pcb = pcb; } } /* compare PCB remote addr+port to UDP source addr+port */ if ((local_match != 0) && (pcb->remote_port == src) && (ip_addr_isany(&pcb->remote_ip) || ip_addr_cmp(&(pcb->remote_ip), &(iphdr->src)))) { /* the first fully matching PCB */ if (prev != NULL) { /* move the pcb to the front of udp_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = udp_pcbs; udp_pcbs = pcb; } else { UDP_STATS_INC(udp.cachehit); } break; } prev = pcb; } /* no fully matching pcb found? then look for an unconnected pcb */ if (pcb == NULL) { pcb = uncon_pcb; } } /* Check checksum if this is a match or if it was directed at us. */ if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, &iphdr->dest)) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\r\n")); #if LWIP_UDPLITE if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { /* Do the UDP Lite checksum */ #if CHECKSUM_CHECK_UDP u16_t chklen = ntohs(udphdr->len); if (chklen < sizeof(struct udp_hdr)) { if (chklen == 0) { /* For UDP-Lite, checksum length of 0 means checksum over the complete packet (See RFC 3828 chap. 3.1) */ chklen = p->tot_len; } else { /* At least the UDP-Lite header must be covered by the checksum! (Again, see RFC 3828 chap. 3.1) */ UDP_STATS_INC(udp.chkerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } } if (inet_chksum_pseudo_partial(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_input: UDP Lite datagram discarded due to failing checksum\r\n")); UDP_STATS_INC(udp.chkerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } #endif /* CHECKSUM_CHECK_UDP */ } else #endif /* LWIP_UDPLITE */ { #if CHECKSUM_CHECK_UDP if (udphdr->chksum != 0) { if (inet_chksum_pseudo(p, (struct ip_addr *)&(iphdr->src), (struct ip_addr *)&(iphdr->dest), IP_PROTO_UDP, p->tot_len) != 0) { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_input: UDP datagram discarded due to failing checksum\r\n")); UDP_STATS_INC(udp.chkerr); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } } #endif /* CHECKSUM_CHECK_UDP */ } if(pbuf_header(p, -UDP_HLEN)) { /* Can we cope with this failing? Just assert for now */ LWIP_ASSERT("pbuf_header failed\r\n", 0); UDP_STATS_INC(udp.drop); snmp_inc_udpinerrors(); pbuf_free(p); goto end; } if (pcb != NULL) { snmp_inc_udpindatagrams(); /* callback */ if (pcb->recv != NULL) { /* now the recv function is responsible for freeing p */ pcb->recv(pcb->recv_arg, pcb, p, &iphdr->src, src); } else { /* no recv function registered? then we have to free the pbuf! */ pbuf_free(p); goto end; } } else { LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\r\n")); #if LWIP_ICMP /* No match was found, send ICMP destination port unreachable unless destination address was broadcast/multicast. */ if (!broadcast && !ip_addr_ismulticast(&iphdr->dest)) { /* move payload pointer back to ip header */ pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); icmp_dest_unreach(p, ICMP_DUR_PORT); } #endif /* LWIP_ICMP */ UDP_STATS_INC(udp.proterr); UDP_STATS_INC(udp.drop); snmp_inc_udpnoports(); pbuf_free(p); } } else { pbuf_free(p); } end: PERF_STOP("udp_input"); }
/** * @ingroup snmp_traps * Sends a generic or enterprise specific trap message. * * @param eoid points to enterprise object identifier * @param generic_trap is the trap code * @param specific_trap used for enterprise traps when generic_trap == 6 * @param varbinds linked list of varbinds to be sent * @return ERR_OK when success, ERR_MEM if we're out of memory * * @note the use of the enterprise identifier field * is per RFC1215. * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps * and .iso.org.dod.internet.private.enterprises.yourenterprise * (sysObjectID) for specific traps. */ err_t snmp_send_trap(const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds) { struct snmp_msg_trap trap_msg; struct snmp_trap_dst *td; struct pbuf *p; u16_t i, tot_len; err_t err = ERR_OK; trap_msg.snmp_version = 0; for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { /* lookup current source address for this dst */ if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) { if (eoid == NULL) { trap_msg.enterprise = snmp_get_device_enterprise_oid(); } else { trap_msg.enterprise = eoid; } trap_msg.gen_trap = generic_trap; if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) { trap_msg.spc_trap = specific_trap; } else { trap_msg.spc_trap = 0; } MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); /* pass 0, calculate length fields */ tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds); tot_len = snmp_trap_header_sum(&trap_msg, tot_len); /* allocate pbuf(s) */ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); if (p != NULL) { struct snmp_pbuf_stream pbuf_stream; snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len); /* pass 1, encode packet into the pbuf(s) */ snmp_trap_header_enc(&trap_msg, &pbuf_stream); snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds); snmp_stats.outtraps++; snmp_stats.outpkts++; /** send to the TRAP destination */ snmp_sendto(snmp_traps_handle, p, &td->dip, LWIP_IANA_PORT_SNMP_TRAP); pbuf_free(p); } else { err = ERR_MEM; } } else { /* routing error */ err = ERR_RTE; } } } return err; }
/** * Sends an generic or enterprise specific trap message. * * @param generic_trap is the trap code * @param eoid points to enterprise object identifier * @param specific_trap used for enterprise traps when generic_trap == 6 * @return ERR_OK when success, ERR_MEM if we're out of memory * * @note the caller is responsible for filling in outvb in the trap_msg * @note the use of the enterprise identifier field * is per RFC1215. * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps * and .iso.org.dod.internet.private.enterprises.yourenterprise * (sysObjectID) for specific traps. */ err_t snmp_send_trap(s8_t generic_trap, const struct snmp_obj_id *eoid, s32_t specific_trap) { struct snmp_trap_dst *td; struct netif *dst_if; const ip_addr_t* dst_ip; struct pbuf *p; u16_t i,tot_len; err_t err = ERR_OK; for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { /* network order trap destination */ ip_addr_copy(trap_msg.dip, td->dip); /* lookup current source address for this dst */ ip_route_get_local_ip(PCB_ISIPV6(trap_msg.pcb), &trap_msg.pcb->local_ip, &td->dip, dst_if, dst_ip); if ((dst_if != NULL) && (dst_ip != NULL)) { trap_msg.sip_raw_len = (IP_IS_V6_VAL(*dst_ip) ? 16 : 4); memcpy(trap_msg.sip_raw, dst_ip, trap_msg.sip_raw_len); trap_msg.gen_trap = generic_trap; trap_msg.spc_trap = specific_trap; if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) { /* enterprise-Specific trap */ trap_msg.enterprise = eoid; } else { /* generic (MIB-II) trap */ mib2_get_snmpgrpid_ptr(&trap_msg.enterprise); } MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); /* pass 0, calculate length fields */ tot_len = snmp_varbind_list_sum(&trap_msg.outvb); tot_len = snmp_trap_header_sum(&trap_msg, tot_len); /* allocate pbuf(s) */ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); if (p != NULL) { u16_t ofs; /* pass 1, encode packet ino the pbuf(s) */ ofs = snmp_trap_header_enc(&trap_msg, p); snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); mib2_inc_snmpouttraps(); mib2_inc_snmpoutpkts(); /** send to the TRAP destination */ udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); pbuf_free(p); } else { err = ERR_MEM; } } else { /* routing error */ err = ERR_RTE; } } } return err; }
/** * Determine if in incoming IP packet is covered by a RAW PCB * and if so, pass it to a user-provided receive callback function. * * Given an incoming IP datagram (as a chain of pbufs) this function * finds a corresponding RAW PCB and calls the corresponding receive * callback function. * * @param p pbuf to be demultiplexed to a RAW PCB. * @param inp network interface on which the datagram was received. * @return - 1 if the packet has been eaten by a RAW PCB receive * callback function. The caller MAY NOT not reference the * packet any longer, and MAY NOT call pbuf_free(). * @return - 0 if packet is not eaten (pbuf is still referenced by the * caller). * */ u8_t raw_input(struct pbuf *p, struct netif *inp) { struct raw_pcb *pcb, *prev; s16_t proto; u8_t eaten = 0; LWIP_UNUSED_ARG(inp); #if LWIP_IPV6 #if LWIP_IPV4 if (IP_HDR_GET_VERSION(p->payload) == 6) #endif /* LWIP_IPV4 */ { struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload; proto = IP6H_NEXTH(ip6hdr); } #if LWIP_IPV4 else #endif /* LWIP_IPV4 */ #endif /* LWIP_IPV6 */ #if LWIP_IPV4 { proto = IPH_PROTO((struct ip_hdr *)p->payload); } #endif /* LWIP_IPV4 */ prev = NULL; pcb = raw_pcbs; /* loop through all raw pcbs until the packet is eaten by one */ /* this allows multiple pcbs to match against the packet by design */ while ((eaten == 0) && (pcb != NULL)) { if ((pcb->protocol == proto) && IP_PCB_IPVER_INPUT_MATCH(pcb) && (ip_addr_isany(&pcb->local_ip) || ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr()))) { #if IP_SOF_BROADCAST_RECV /* broadcast filter? */ if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp)) #if LWIP_IPV6 || PCB_ISIPV6(pcb) #endif /* LWIP_IPV6 */ ) #endif /* IP_SOF_BROADCAST_RECV */ { /* receive callback function available? */ if (pcb->recv != NULL) { #ifndef LWIP_NOASSERT void* old_payload = p->payload; #endif /* the receive callback function did not eat the packet? */ eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr()); if (eaten != 0) { /* receive function ate the packet */ p = NULL; eaten = 1; if (prev != NULL) { /* move the pcb to the front of raw_pcbs so that is found faster next time */ prev->next = pcb->next; pcb->next = raw_pcbs; raw_pcbs = pcb; } } else { /* sanity-check that the receive callback did not alter the pbuf */ LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet", p->payload == old_payload); } } /* no receive callback function was set for this raw PCB */ } /* drop the packet */ } prev = pcb; pcb = pcb->next; } return eaten; }
err_t ip_input(struct pbuf *p, struct netif *inp) { struct ip_hdr *iphdr; struct netif *netif; u16_t iphdrlen; IP_STATS_INC(ip.recv); snmp_inc_ipinreceives(); /* identify the IP header */ iphdr = p->payload; if (IPH_V(iphdr) != 4) { LWIP_DEBUGF(IP_DEBUG | 1, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr))); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.err); IP_STATS_INC(ip.drop); snmp_inc_ipinhdrerrors(); return ERR_OK; } /* obtain IP header length in number of 32-bit words */ iphdrlen = IPH_HL(iphdr); /* calculate IP header length in bytes */ iphdrlen *= 4; /* header length exceeds first pbuf length? */ if (iphdrlen > p->len) { LWIP_DEBUGF(IP_DEBUG | 2, ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet droppped.\n", iphdrlen, p->len)); /* free (drop) packet pbufs */ pbuf_free(p); IP_STATS_INC(ip.lenerr); IP_STATS_INC(ip.drop); snmp_inc_ipindiscards(); return ERR_OK; } /* verify checksum */ #if CHECKSUM_CHECK_IP if (inet_chksum(iphdr, iphdrlen) != 0) { LWIP_DEBUGF(IP_DEBUG | 2, ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdrlen))); ip_debug_print(p); pbuf_free(p); IP_STATS_INC(ip.chkerr); IP_STATS_INC(ip.drop); snmp_inc_ipinhdrerrors(); return ERR_OK; } #endif /* Trim pbuf. This should have been done at the netif layer, * but we'll do it anyway just to be sure that its done. */ pbuf_realloc(p, ntohs(IPH_LEN(iphdr))); /* match packet against an interface, i.e. is this packet for us? */ for (netif = netif_list; netif != NULL; netif = netif->next) { LWIP_DEBUGF(IP_DEBUG, ("ip_input: iphdr->dest 0x%"X32_F" netif->ip_addr 0x%"X32_F" (0x%"X32_F", 0x%"X32_F", 0x%"X32_F")\n", iphdr->dest.addr, netif->ip_addr.addr, iphdr->dest.addr & netif->netmask.addr, netif->ip_addr.addr & netif->netmask.addr, iphdr->dest.addr & ~(netif->netmask.addr))); /* interface is up and configured? */ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) { /* unicast to this interface address? */ if (ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr)) || /* or broadcast on this interface network address? */ ip_addr_isbroadcast(&(iphdr->dest), netif)) { LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n", netif->name[0], netif->name[1])); /* break out of for loop */ break; } } } #if LWIP_DHCP /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed * using link layer addressing (such as Ethernet MAC) so we must not filter on IP. * According to RFC 1542 section 3.1.1, referred by RFC 2131). */ if (netif == NULL) { /* remote port is DHCP server? */ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) { LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: UDP packet to DHCP client port %"U16_F"\n", ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest))); if (ntohs(((struct udp_hdr *)((u8_t *)iphdr + iphdrlen))->dest) == DHCP_CLIENT_PORT) { LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: DHCP packet accepted.\n")); netif = inp; } } } #endif /* LWIP_DHCP */ /* packet not for us? */ if (netif == NULL) { /* packet not for us, route or discard */ LWIP_DEBUGF(IP_DEBUG | DBG_TRACE | 1, ("ip_input: packet not for us.\n")); #if IP_FORWARD /* non-broadcast packet? */ if (!ip_addr_isbroadcast(&(iphdr->dest), inp)) { /* try to forward IP packet on (other) interfaces */ ip_forward(p, iphdr, inp); } else #endif /* IP_FORWARD */ { snmp_inc_ipinaddrerrors(); snmp_inc_ipindiscards(); } pbuf_free(p); return ERR_OK; } /* packet consists of multiple fragments? */ if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { #if IP_REASSEMBLY /* packet fragment reassembly code present? */ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n", ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & htons(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8)); /* reassemble the packet*/ p = ip_reass(p); /* packet not fully reassembled yet? */ if (p == NULL) { return ERR_OK; } iphdr = p->payload; #else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */ pbuf_free(p); LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n", ntohs(IPH_OFFSET(iphdr)))); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); /* unsupported protocol feature */ snmp_inc_ipinunknownprotos(); return ERR_OK; #endif /* IP_REASSEMBLY */ } #if IP_OPTIONS == 0 /* no support for IP options in the IP header? */ if (iphdrlen > IP_HLEN) { LWIP_DEBUGF(IP_DEBUG | 2, ("IP packet dropped since there were IP options (while IP_OPTIONS == 0).\n")); pbuf_free(p); IP_STATS_INC(ip.opterr); IP_STATS_INC(ip.drop); /* unsupported protocol feature */ snmp_inc_ipinunknownprotos(); return ERR_OK; } #endif /* IP_OPTIONS == 0 */ /* send to upper layers */ LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n")); ip_debug_print(p); LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len)); #if LWIP_RAW /* raw input did not eat the packet? */ if (raw_input(p, inp) == 0) { #endif /* LWIP_RAW */ switch (IPH_PROTO(iphdr)) { #if LWIP_UDP case IP_PROTO_UDP: case IP_PROTO_UDPLITE: snmp_inc_ipindelivers(); udp_input(p, inp); break; #endif /* LWIP_UDP */ #if LWIP_TCP case IP_PROTO_TCP: snmp_inc_ipindelivers(); tcp_input(p, inp); break; #endif /* LWIP_TCP */ case IP_PROTO_ICMP: snmp_inc_ipindelivers(); icmp_input(p, inp); break; default: /* send ICMP destination protocol unreachable unless is was a broadcast */ if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && !ip_addr_ismulticast(&(iphdr->dest))) { p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } pbuf_free(p); LWIP_DEBUGF(IP_DEBUG | 2, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr))); IP_STATS_INC(ip.proterr); IP_STATS_INC(ip.drop); snmp_inc_ipinunknownprotos(); } #if LWIP_RAW } /* LWIP_RAW */ #endif return ERR_OK; }
/** * Sends an generic or enterprise specific trap message. * * @param generic_trap is the trap code * @param eoid points to enterprise object identifier * @param specific_trap used for enterprise traps when generic_trap == 6 * @return ERR_OK when success, ERR_MEM if we're out of memory * * @note the caller is responsible for filling in outvb in the trap_msg * @note the use of the enterpise identifier field * is per RFC1215. * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps * and .iso.org.dod.internet.private.enterprises.yourenterprise * (sysObjectID) for specific traps. */ err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap) { struct snmp_trap_dst *td; struct netif *dst_if; ip_addr_t dst_ip; struct pbuf *p; u16_t i,tot_len; for (i=0, td = &trap_dst[0]; i<SNMP_TRAP_DESTINATIONS; i++, td++) { if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { /* network order trap destination */ ip_addr_copy(trap_msg.dip, td->dip); /* lookup current source address for this dst */ dst_if = ip_route(&td->dip); ip_addr_copy(dst_ip, dst_if->ip_addr); /* @todo: what about IPv6? */ trap_msg.sip_raw[0] = ip4_addr1(&dst_ip); trap_msg.sip_raw[1] = ip4_addr2(&dst_ip); trap_msg.sip_raw[2] = ip4_addr3(&dst_ip); trap_msg.sip_raw[3] = ip4_addr4(&dst_ip); trap_msg.gen_trap = generic_trap; trap_msg.spc_trap = specific_trap; if (generic_trap == SNMP_GENTRAP_ENTERPRISESPC) { /* enterprise-Specific trap */ trap_msg.enterprise = eoid; } else { /* generic (MIB-II) trap */ snmp_get_snmpgrpid_ptr(&trap_msg.enterprise); } snmp_get_sysuptime(&trap_msg.ts); /* pass 0, calculate length fields */ tot_len = snmp_varbind_list_sum(&trap_msg.outvb); tot_len = snmp_trap_header_sum(&trap_msg, tot_len); /* allocate pbuf(s) */ p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL); if (p != NULL) { u16_t ofs; /* pass 1, encode packet ino the pbuf(s) */ ofs = snmp_trap_header_enc(&trap_msg, p); snmp_varbind_list_enc(&trap_msg.outvb, p, ofs); snmp_inc_snmpouttraps(); snmp_inc_snmpoutpkts(); /** send to the TRAP destination */ udp_sendto(trap_msg.pcb, p, &trap_msg.dip, SNMP_TRAP_PORT); pbuf_free(p); } else { return ERR_MEM; } } } return ERR_OK; }
/** * Send the raw IP packet to the given address. Note that actually you cannot * modify the IP headers (this is inconsistent with the receive callback where * you actually get the IP headers), you can only specify the IP payload here. * It requires some more changes in lwIP. (there will be a raw_send() function * then.) * * @param pcb the raw pcb which to send * @param p the IP payload to send * @param ipaddr the destination address of the IP packet * */ err_t raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr) { err_t err; struct netif *netif; const ip_addr_t *src_ip; struct pbuf *q; /* q will be sent down the stack */ s16_t header_size; const ip_addr_t *dst_ip = ipaddr; if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) { return ERR_VAL; } LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); header_size = ( #if LWIP_IPV4 && LWIP_IPV6 IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN); #elif LWIP_IPV4 IP_HLEN); #else IP6_HLEN); #endif /* not enough space to add an IP header to first pbuf in given p chain? */ if (pbuf_header(p, header_size)) { /* allocate header in new pbuf */ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); return ERR_MEM; } if (p->tot_len != 0) { /* chain header q in front of given pbuf p */ pbuf_chain(q, p); } /* { first pbuf q points to header pbuf } */ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { /* first pbuf q equals given pbuf */ q = p; if (pbuf_header(q, -header_size)) { LWIP_ASSERT("Can't restore header we just removed!", 0); return ERR_MEM; } } netif = ip_route(&pcb->local_ip, dst_ip); if (netif == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to ")); ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_RTE; } #if IP_SOF_BROADCAST if (!IP_IS_V6(ipaddr)) { /* broadcast filter? */ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_VAL; } } #endif /* IP_SOF_BROADCAST */ if (ip_addr_isany(&pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = ip_netif_get_local_ip(netif, dst_ip); #if LWIP_IPV6 if (src_ip == NULL) { if (q != p) { pbuf_free(q); } return ERR_RTE; } #endif /* LWIP_IPV6 */ } else { /* use RAW PCB local IP address as source address */ src_ip = &pcb->local_ip; } #if LWIP_IPV6 /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542, compute the checksum and update the checksum in the payload. */ if (IP_IS_V6(dst_ip) && pcb->chksum_reqd) { u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip)); LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2)); SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t)); } #endif NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint); err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, pcb->protocol, netif); NETIF_SET_HWADDRHINT(netif, NULL); /* did we chain a header earlier? */ if (q != p) { /* free the header */ pbuf_free(q); } return err; }
/** * This function is called by the network interface device driver when * an IP packet is received. The function does the basic checks of the * IP header such as packet size being at least larger than the header * size etc. If the packet was not destined for us, the packet is * forwarded (using ip_forward). The IP checksum is always checked. * * Finally, the packet is sent to the upper layer protocol input function. * * @param p the received IP packet (p->payload points to IP header) * @param inp the netif on which this packet was received * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't * processed, but currently always returns ERR_OK) */ uint8 ip_input(PBUF *p, NETIF *inp) { IP_HDR *iphdr; NETIF *netif; uint16 iphdr_hlen; uint16 iphdr_len; /* identify the IP header */ iphdr = p->payload; if (IPH_V(iphdr) != 4) { pbuf_free(p);//this packet is not ipv4 return ERR_OK; } /* obtain IP header length in number of 32-bit words */ iphdr_hlen = IPH_HL(iphdr); /* calculate IP header length in bytes */ iphdr_hlen *= 4; /* obtain ip pocket total length in bytes */ iphdr_len = ntohs(IPH_LEN(iphdr)); /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */ if ((iphdr_hlen > p->len) || (iphdr_len > p->len)) { /* free (drop) packet pbufs */ pbuf_free(p); return ERR_OK; } if (inet_chksum(iphdr, iphdr_hlen) != 0) { pbuf_free(p);//ip header checksum error! return ERR_OK; } netif = inp; /* interface is up and configured? */ if (ip_addr_isany(&(netif->ip_addr))) { netif = NULL; } else { /* unicast to this interface address? */ if (!ip_addr_cmp(&(iphdr->dest), &(netif->ip_addr))) { netif = NULL; } } /* packet consists of multiple fragments? */ if ((IPH_OFFSET(iphdr) & htons(IP_OFFMASK | IP_MF)) != 0) { printf("received splited IP packet!! \n"); pbuf_free(p); return ERR_OK; } if (iphdr_hlen > IP_HLEN) { pbuf_free(p); return ERR_OK; } switch (IPH_PROTO(iphdr)) { case IP_PROTO_TCP://(6) //printf("get a tcp packet\n"); tcp_input(p, inp); break; case IP_PROTO_UDP://(17) printf("UDP\n"); udp_input(p, inp); break; case IP_PROTO_ICMP://(1) printf("gICMP\n"); icmp_input(p, inp); break; default: /* send ICMP destination protocol unreachable unless is was a broadcast */ printf("gerr\n"); if (!ip_addr_isbroadcast(&(iphdr->dest), inp) && !ip_addr_ismulticast(&(iphdr->dest))) { p->payload = iphdr; icmp_dest_unreach(p, ICMP_DUR_PROTO); } pbuf_free(p); } return ERR_OK; }
/** * Send the raw IP packet to the given address. Note that actually you cannot * modify the IP headers (this is inconsistent with the receive callback where * you actually get the IP headers), you can only specify the IP payload here. * It requires some more changes in lwIP. (there will be a raw_send() function * then.) * * @param pcb the raw pcb which to send * @param p the IP payload to send * @param ipaddr the destination address of the IP packet * */ err_t raw_sendto(struct raw_pcb *pcb, struct pbuf *p, struct ip_addr *ipaddr) { err_t err; struct netif *netif; struct ip_addr *src_ip; struct pbuf *q; /* q will be sent down the stack */ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n")); /* not enough space to add an IP header to first pbuf in given p chain? */ if (pbuf_header(p, IP_HLEN)) { /* allocate header in new pbuf */ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM); /* new header pbuf could not be allocated? */ if (q == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n")); return ERR_MEM; } /* chain header q in front of given pbuf p */ pbuf_chain(q, p); /* { first pbuf q points to header pbuf } */ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); } else { /* first pbuf q equals given pbuf */ q = p; if(pbuf_header(q, -IP_HLEN)) { LWIP_ASSERT("Can't restore header we just removed!", 0); return ERR_MEM; } } if ((netif = ip_route(ipaddr)) == NULL) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to 0x%"X32_F"\n", ipaddr->addr)); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_RTE; } #if IP_SOF_BROADCAST /* broadcast filter? */ if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(ipaddr, netif) ) { LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); /* free any temporary header pbuf allocated by pbuf_header() */ if (q != p) { pbuf_free(q); } return ERR_VAL; } #endif /* IP_SOF_BROADCAST */ if (ip_addr_isany(&pcb->local_ip)) { /* use outgoing network interface IP address as source address */ src_ip = &(netif->ip_addr); } else { /* use RAW PCB local IP address as source address */ src_ip = &(pcb->local_ip); } #if LWIP_NETIF_HWADDRHINT netif->addr_hint = &(pcb->addr_hint); #endif /* LWIP_NETIF_HWADDRHINT*/ err = ip_output_if (q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif); #if LWIP_NETIF_HWADDRHINT netif->addr_hint = NULL; #endif /* LWIP_NETIF_HWADDRHINT*/ /* did we chain a header earlier? */ if (q != p) { /* free the header */ pbuf_free(q); } return err; }
/** * Update (or insert) a IP/MAC address pair in the ARP cache. * * If a pending entry is resolved, any queued packets will be sent * at this point. * * @param netif netif related to this entry (used for NETIF_ADDRHINT) * @param ipaddr IP address of the inserted ARP entry. * @param ethaddr Ethernet address of the inserted ARP entry. * @param flags @see definition of ETHARP_FLAG_* * * @return * - ERR_OK Succesfully updated ARP cache. * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set. * - ERR_ARG Non-unicast address given, those will not appear in ARP cache. * * @see pbuf_free() */ static err_t update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags) { s8_t i; LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); /* non-unicast address? */ if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, netif) || ip_addr_ismulticast(ipaddr)) { LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: will not add non-unicast IP address to ARP cache\n")); return ERR_ARG; } /* find or create ARP entry */ i = find_entry(ipaddr, flags); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; } #if ETHARP_SUPPORT_STATIC_ENTRIES if (flags & ETHARP_FLAG_STATIC_ENTRY) { /* record static type */ arp_table[i].static_entry = 1; } #endif /* ETHARP_SUPPORT_STATIC_ENTRIES */ /* mark it stable */ arp_table[i].state = ETHARP_STATE_STABLE; #if LWIP_SNMP /* record network interface */ arp_table[i].netif = netif; #endif /* LWIP_SNMP */ /* insert in SNMP ARP index tree */ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr); LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i)); /* update address */ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr); /* reset time stamp */ arp_table[i].ctime = 0; /* this is where we will send out queued packets! */ #if ARP_QUEUEING while (arp_table[i].q != NULL) { struct pbuf *p; /* remember remainder of queue */ struct etharp_q_entry *q = arp_table[i].q; /* pop first item off the queue */ arp_table[i].q = q->next; /* get the packet pointer */ p = q->p; /* now queue entry can be freed */ memp_free(MEMP_ARP_QUEUE, q); #else /* ARP_QUEUEING */ if (arp_table[i].q != NULL) { struct pbuf *p = arp_table[i].q; arp_table[i].q = NULL; #endif /* ARP_QUEUEING */ /* send the queued IP packet */ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr); /* free the queued IP packet */ pbuf_free(p); } return ERR_OK; } #if ETHARP_SUPPORT_STATIC_ENTRIES /** Add a new static entry to the ARP table. If an entry exists for the * specified IP address, this entry is overwritten. * If packets are queued for the specified IP address, they are sent out. * * @param ipaddr IP address for the new static entry * @param ethaddr ethernet address for the new static entry * @return @see return values of etharp_add_static_entry */ err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr) { struct netif *netif; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr), ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2], ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5])); netif = ip_route(ipaddr); if (netif == NULL) { return ERR_RTE; } return update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY); } /** Remove a static entry from the ARP table previously added with a call to * etharp_add_static_entry. * * @param ipaddr IP address of the static entry to remove * @return ERR_OK: entry removed * ERR_MEM: entry wasn't found * ERR_ARG: entry wasn't a static entry but a dynamic one */ err_t etharp_remove_static_entry(ip_addr_t *ipaddr) { s8_t i; LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr))); /* find or create ARP entry */ i = find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY); /* bail out if no entry could be found */ if (i < 0) { return (err_t)i; } if ((arp_table[i].state != ETHARP_STATE_STABLE) || (arp_table[i].static_entry == 0)) { /* entry wasn't a static entry, cannot remove it */ return ERR_ARG; } /* entry found, free it */ free_entry(i); return ERR_OK; }
/** * Bind an UDP PCB. * * @param pcb UDP PCB to be bound with a local address ipaddr and port. * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to * bind to all local interfaces. * @param port local UDP port to bind with. Use 0 to automatically bind * to a random port between UDP_LOCAL_PORT_RANGE_START and * UDP_LOCAL_PORT_RANGE_END. * * ipaddr & port are expected to be in the same byte order as in the pcb. * * @return lwIP error code. * - ERR_OK. Successful. No error occured. * - ERR_USE. The specified ipaddr and port are already bound to by * another UDP PCB. * * @see udp_disconnect() */ err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port) { struct udp_pcb *ipcb; u8_t rebind; LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); ip_addr_debug_print(UDP_DEBUG, ipaddr); LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); rebind = 0; /* Check for double bind and rebind of the same pcb */ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { /* is this UDP PCB already on active list? */ if (pcb == ipcb) { /* pcb may occur at most once in active list */ LWIP_ASSERT("rebind == 0", rebind == 0); /* pcb already in list, just rebind */ rebind = 1; } /* this code does not allow upper layer to share a UDP port for listening to broadcast or multicast traffic (See SO_REUSE_ADDR and SO_REUSE_PORT under *BSD). TODO: See where it fits instead, OR combine with implementation of UDP PCB flags. Leon Woestenberg. */ #ifdef LWIP_UDP_TODO /* port matches that of PCB in list? */ else if ((ipcb->local_port == port) && /* IP address matches, or one is IP_ADDR_ANY? */ (ip_addr_isany(&(ipcb->local_ip)) || ip_addr_isany(ipaddr) || ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { /* other PCB already binds to this local IP and port */ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); return ERR_USE; } #endif } ip_addr_set(&pcb->local_ip, ipaddr); /* no port specified? */ if (port == 0) { #ifndef UDP_LOCAL_PORT_RANGE_START #define UDP_LOCAL_PORT_RANGE_START 4096 #define UDP_LOCAL_PORT_RANGE_END 0x7fff #endif port = UDP_LOCAL_PORT_RANGE_START; ipcb = udp_pcbs; while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { if (ipcb->local_port == port) { /* port is already used by another udp_pcb */ port++; /* restart scanning all udp pcbs */ ipcb = udp_pcbs; } else /* go on with next udp pcb */ ipcb = ipcb->next; } if (ipcb != NULL) { /* no more ports available in local range */ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); return ERR_USE; } } pcb->local_port = port; snmp_insert_udpidx_tree(pcb); /* pcb not active yet? */ if (rebind == 0) { /* place the PCB on the active list if not already there */ pcb->next = udp_pcbs; udp_pcbs = pcb; } LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", (u16_t)((ntohl(pcb->local_ip.addr) >> 24) & 0xff), (u16_t)((ntohl(pcb->local_ip.addr) >> 16) & 0xff), (u16_t)((ntohl(pcb->local_ip.addr) >> 8) & 0xff), (u16_t)(ntohl(pcb->local_ip.addr) & 0xff), pcb->local_port)); return ERR_OK; }
err_t ether_output(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr) { struct pbuf *q; struct eth_hdr *ethhdr; struct eth_addr *dest, mcastaddr; struct ip_addr *queryaddr; err_t err; int i; int loopback = 0; //kprintf("ether: xmit %d bytes, %d bufs\n", p->tot_len, pbuf_clen(p)); if ((netif->flags & NETIF_UP) == 0) return -ENETDOWN; if (pbuf_header(p, ETHER_HLEN)) { kprintf(KERN_ERR "ether_output: not enough room for Ethernet header in pbuf\n"); stats.link.err++; return -EBUF; } // Construct Ethernet header. Start with looking up deciding which // MAC address to use as a destination address. Broadcasts and // multicasts are special, all other addresses are looked up in the // ARP table. queryaddr = ipaddr; if (ip_addr_isany(ipaddr) || ip_addr_isbroadcast(ipaddr, &netif->netmask)) { dest = (struct eth_addr *) ðbroadcast; } else if (ip_addr_ismulticast(ipaddr)) { // Hash IP multicast address to MAC address. mcastaddr.addr[0] = 0x01; mcastaddr.addr[1] = 0x0; mcastaddr.addr[2] = 0x5e; mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f; mcastaddr.addr[4] = ip4_addr3(ipaddr); mcastaddr.addr[5] = ip4_addr4(ipaddr); dest = &mcastaddr; } else if (ip_addr_cmp(ipaddr, &netif->ipaddr)) { dest = &netif->hwaddr; loopback = 1; } else { if (ip_addr_maskcmp(ipaddr, &netif->ipaddr, &netif->netmask)) { // Use destination IP address if the destination is on the same subnet as we are. queryaddr = ipaddr; } else { // Otherwise we use the default router as the address to send the Ethernet frame to. queryaddr = &netif->gw; } dest = arp_lookup(queryaddr); } // If the arp_lookup() didn't find an address, we send out an ARP query for the IP address. if (dest == NULL) { q = arp_query(netif, &netif->hwaddr, queryaddr); if (q != NULL) { err = dev_transmit((dev_t) netif->state, q); if (err < 0) { kprintf(KERN_ERR "ether: error %d sending arp packet\n", err); pbuf_free(q); stats.link.drop++; return err; } } // Queue packet for transmission, when the ARP reply returns err = arp_queue(netif, p, queryaddr); if (err < 0) { kprintf(KERN_ERR "ether: error %d queueing packet\n", err); stats.link.drop++; stats.link.memerr++; return err; } return 0; } ethhdr = p->payload; for (i = 0; i < 6; i++) { ethhdr->dest.addr[i] = dest->addr[i]; ethhdr->src.addr[i] = netif->hwaddr.addr[i]; } ethhdr->type = htons(ETHTYPE_IP); if (loopback) { struct pbuf *q; q = pbuf_dup(PBUF_RAW, p); if (!q) return -ENOMEM; err = ether_input(netif, q); if (err < 0) { pbuf_free(q); return err; } } else { err = dev_transmit((dev_t) netif->state, p); if (err < 0) { kprintf(KERN_ERR "ether: error %d sending packet\n", err); return err; } } return 0; }