/** * Outbound TTL/HOPL check. */ static int pxudp_ttl_expired(struct pbuf *p) { int ttl; if (ip_current_is_v6()) { ttl = IP6H_HOPLIM(ip6_current_header()); } else { ttl = IPH_TTL(ip_current_header()); } if (RT_UNLIKELY(ttl <= 1)) { int status = pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN); if (RT_LIKELY(status == 0)) { if (ip_current_is_v6()) { icmp6_time_exceeded(p, ICMP6_TE_HL); } else { icmp_time_exceeded(p, ICMP_TE_TTL); } } pbuf_free(p); return 1; } return 0; }
/* * Forward an IP packet. It finds an appropriate route for the packet, * decrements the TTL value of the packet, adjusts the checksum and outputs * the packet on the appropriate interface. */ static void ip_forward (ip_t *ip, buf_t *p, ip_addr_const gateway, netif_t *netif, ip_addr_const netif_ipaddr) { ip_hdr_t *iphdr = (ip_hdr_t*) p->payload; /* Decrement TTL and send ICMP if ttl == 0. */ if (iphdr->ttl <= 1) { /* Don't send ICMP messages in response to ICMP messages */ if (iphdr->proto != IP_PROTO_ICMP) { icmp_time_exceeded (ip, p); } return; } iphdr->ttl--; /* Incremental update of the IP checksum. */ if (iphdr->chksum_h != 0xff) iphdr->chksum_h += 1; else if (iphdr->chksum_l != 0xff) { iphdr->chksum_h = 0; iphdr->chksum_l += 1; } else { iphdr->chksum_h = 1; iphdr->chksum_l = 0; } /* Forwarding packet to netif. */ if (! gateway) gateway = iphdr->dest.var; netif_output (netif, p, gateway, netif_ipaddr); ++ip->forw_datagrams; }
/** * Forwards an IP packet. It finds an appropriate route for the * packet, decrements the TTL value of the packet, adjusts the * checksum and outputs the packet on the appropriate interface. * * @param p the packet to forward (p->payload points to IP header) * @param iphdr the IP header of the input packet * @param inp the netif on which this packet was received * @return the netif on which the packet was sent (NULL if it wasn't sent) */ static struct netif * ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) { struct netif *netif; PERF_START; /* Find network interface where to forward this IP packet to. */ netif = ip_route((struct ip_addr *)&(iphdr->dest)); if (netif == NULL) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%"X32_F" found\n", iphdr->dest.addr)); snmp_inc_ipoutnoroutes(); return (struct netif *)NULL; } /* Do not forward packets onto the same network interface on which * they arrived. */ if (netif == inp) { LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n")); snmp_inc_ipoutnoroutes(); return (struct netif *)NULL; } /* decrement TTL */ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); /* send ICMP if TTL == 0 */ if (IPH_TTL(iphdr) == 0) { snmp_inc_ipinhdrerrors(); #if LWIP_ICMP /* Don't send ICMP messages in response to ICMP messages */ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) { icmp_time_exceeded(p, ICMP_TE_TTL); } #endif /* LWIP_ICMP */ return (struct netif *)NULL; } /* Incrementally update the IP checksum. */ if (IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) { IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); } else { IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100)); } LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%"X32_F"\n", iphdr->dest.addr)); IP_STATS_INC(ip.fw); IP_STATS_INC(ip.xmit); snmp_inc_ipforwdatagrams(); PERF_STOP("ip_forward"); /* transmit pbuf on chosen interface */ netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); return netif; }
/** * Free a datagram (struct ip_reassdata) and all its pbufs. * Updates the total count of enqueued pbufs (ip_reass_pbufcount), * SNMP counters and sends an ICMP time exceeded packet. * * @param ipr datagram to free * @param prev the previous datagram in the linked list * @return the number of pbufs freed */ static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev) { u16_t pbufs_freed = 0; u8_t clen; struct pbuf *p; struct ip_reass_helper *iprh; LWIP_ASSERT("prev != ipr", prev != ipr); if (prev != NULL) { LWIP_ASSERT("prev->next == ipr", prev->next == ipr); } snmp_inc_ipreasmfails(); #if LWIP_ICMP iprh = (struct ip_reass_helper *)ipr->p->payload; if (iprh->start == 0) { /* The first fragment was received, send ICMP time exceeded. */ /* First, de-queue the first pbuf from r->p. */ p = ipr->p; ipr->p = iprh->next_pbuf; /* Then, copy the original header into it. */ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN); icmp_time_exceeded(p, ICMP_TE_FRAG); clen = pbuf_clen(p); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); pbufs_freed += clen; pbuf_free(p); } #endif /* LWIP_ICMP */ /* First, free all received pbufs. The individual pbufs need to be released separately as they have not yet been chained */ p = ipr->p; while (p != NULL) { struct pbuf *pcur; iprh = (struct ip_reass_helper *)p->payload; pcur = p; /* get the next pointer before freeing */ p = iprh->next_pbuf; clen = pbuf_clen(pcur); LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff); pbufs_freed += clen; pbuf_free(pcur); } /* Then, unchain the struct ip_reassdata from the list and free it. */ ip_reass_dequeue_datagram(ipr, prev); LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed); ip_reass_pbufcount -= pbufs_freed; return pbufs_freed; }
/*-----------------------------------------------------------------------------------*/ static void ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp) { static struct netif *netif; PERF_START; if((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for 0x%lx found\n", iphdr->dest.addr)); return; } /* Don't forward packets onto the same network interface on which they arrived. */ if(netif == inp) { DEBUGF(IP_DEBUG, ("ip_forward: not forward packets back on incoming interface.\n")); return; } /* Decrement TTL and send ICMP if ttl == 0. */ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1); if(IPH_TTL(iphdr) == 0) { /* Don't send ICMP messages in response to ICMP messages */ if(IPH_PROTO(iphdr) != IP_PROTO_ICMP) { icmp_time_exceeded(p, ICMP_TE_TTL); } return; } /* Incremental update of the IP checksum. */ if(IPH_CHKSUM(iphdr) >= htons(0xffff - 0x100)) { IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100) + 1); } else { IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + htons(0x100)); } DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to 0x%lx\n", iphdr->dest.addr)); #ifdef IP_STATS ++stats.ip.fw; ++stats.ip.xmit; #endif /* IP_STATS */ PERF_STOP("ip_forward"); netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); }
static void ip_forward(struct pbuf *p, struct ip_hdr *iphdr) { struct netif *netif; PERF_START; if ((netif = ip_route((struct ip_addr *)&(iphdr->dest))) == NULL) { LWIP_DEBUGF(IP_DEBUG, ("ip_input: no forwarding route found for ")); #if IP_DEBUG ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); #endif /* IP_DEBUG */ LWIP_DEBUGF(IP_DEBUG, ("\n")); pbuf_free(p); return; } /* Decrement TTL and send ICMP if ttl == 0. */ if (--iphdr->hoplim == 0) { #if LWIP_ICMP /* Don't send ICMP messages in response to ICMP messages */ if (iphdr->nexthdr != IP_PROTO_ICMP) { icmp_time_exceeded(p, ICMP_TE_TTL); } #endif /* LWIP_ICMP */ pbuf_free(p); return; } /* Incremental update of the IP checksum. */ /* if (iphdr->chksum >= htons(0xffff - 0x100)) { iphdr->chksum += htons(0x100) + 1; } else { iphdr->chksum += htons(0x100); }*/ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to ")); #if IP_DEBUG ip_addr_debug_print(IP_DEBUG, ((struct ip_addr *)&(iphdr->dest))); #endif /* IP_DEBUG */ LWIP_DEBUGF(IP_DEBUG, ("\n")); IP_STATS_INC(ip.fw); IP_STATS_INC(ip.xmit); PERF_STOP("ip_forward"); netif->output(netif, p, (struct ip_addr *)&(iphdr->dest)); }
/** * ICMP Echo Request in pbuf "p" is to be proxied. */ static void pxping_recv4(void *arg, struct pbuf *p) { struct pxping *pxping = (struct pxping *)arg; const struct ip_hdr *iph; const struct icmp_echo_hdr *icmph; u16_t iphlen; size_t bufsize; struct pong4 *pong; IPAddr dst; int mapped; int ttl; IP_OPTION_INFORMATION opts; void *reqdata; size_t reqsize; int status; pong = NULL; iphlen = ip_current_header_tot_len(); if (RT_UNLIKELY(iphlen != IP_HLEN)) { /* we don't do options */ goto out; } iph = (const struct ip_hdr *)ip_current_header(); icmph = (const struct icmp_echo_hdr *)p->payload; mapped = pxremap_outbound_ip4((ip_addr_t *)&dst, (ip_addr_t *)&iph->dest); if (RT_UNLIKELY(mapped == PXREMAP_FAILED)) { goto out; } ttl = IPH_TTL(iph); if (mapped == PXREMAP_ASIS) { if (RT_UNLIKELY(ttl == 1)) { status = pbuf_header(p, iphlen); /* back to IP header */ if (RT_LIKELY(status == 0)) { icmp_time_exceeded(p, ICMP_TE_TTL); } goto out; } --ttl; } status = pbuf_header(p, -(u16_t)sizeof(*icmph)); /* to ping payload */ if (RT_UNLIKELY(status != 0)) { goto out; } bufsize = sizeof(ICMP_ECHO_REPLY) + p->tot_len; pong = (struct pong4 *)malloc(sizeof(*pong) - sizeof(pong->buf) + bufsize); if (RT_UNLIKELY(pong == NULL)) { goto out; } pong->bufsize = bufsize; pong->netif = pxping->netif; memcpy(&pong->reqiph, iph, sizeof(*iph)); memcpy(&pong->reqicmph, icmph, sizeof(*icmph)); reqsize = p->tot_len; if (p->next == NULL) { /* single pbuf can be directly used as request data source */ reqdata = p->payload; } else { /* data from pbuf chain must be concatenated */ pbuf_copy_partial(p, pong->buf, p->tot_len, 0); reqdata = pong->buf; } opts.Ttl = ttl; opts.Tos = IPH_TOS(iph); /* affected by DisableUserTOSSetting key */ opts.Flags = (IPH_OFFSET(iph) & PP_HTONS(IP_DF)) != 0 ? IP_FLAG_DF : 0; opts.OptionsSize = 0; opts.OptionsData = 0; status = IcmpSendEcho2(pxping->hdl4, NULL, pxping->callback4, pong, dst, reqdata, (WORD)reqsize, &opts, pong->buf, (DWORD)pong->bufsize, 5 * 1000 /* ms */); if (RT_UNLIKELY(status != 0)) { DPRINTF(("IcmpSendEcho2: unexpected status %d\n", status)); goto out; } else if ((status = GetLastError()) != ERROR_IO_PENDING) { int code; DPRINTF(("IcmpSendEcho2: error %d\n", status)); switch (status) { case ERROR_NETWORK_UNREACHABLE: code = ICMP_DUR_NET; break; case ERROR_HOST_UNREACHABLE: code = ICMP_DUR_HOST; break; default: code = -1; break; } if (code != -1) { /* move payload back to IP header */ status = pbuf_header(p, (u16_t)(sizeof(*icmph) + iphlen)); if (RT_LIKELY(status == 0)) { icmp_dest_unreach(p, code); } } goto out; } pong = NULL; /* callback owns it now */ out: if (pong != NULL) { free(pong); } pbuf_free(p); }