static int icmp_notify_an_error(const struct icmphdr *icmph, const void *msg, size_t msg_sz, uint16_t extra_info, int only_raw, struct sk_buff *skb) { const struct iphdr *emb_iph; uint32_t error_info; emb_iph = msg; assert(emb_iph != NULL); if ((msg_sz < IP_MIN_HEADER_SIZE) || (IP_HEADER_SIZE(emb_iph) < IP_MIN_HEADER_SIZE) || (ntohs(emb_iph->tot_len) < IP_HEADER_SIZE(emb_iph)) || (msg_sz < IP_HEADER_SIZE(emb_iph))) { log_error("invalid length"); skb_free(skb); return 0; /* error: invalid length */ } if (!ip_check_version(emb_iph)) { log_error("not ipv4"); skb_free(skb); return 0; /* error: not ipv4 */ } if (ip_hdr(skb)->daddr != emb_iph->saddr) { log_error("not my embedded packet"); skb_free(skb); return 0; /* error: not my embedded packet */ } error_info = extra_info << 16 | icmph->code << 8 | icmph->type; raw_err(skb, error_info); if (!only_raw) { const struct net_proto *nproto; nproto = net_proto_lookup(ETH_P_IP, emb_iph->proto); if (nproto != NULL) { assert(nproto->handle_error != NULL); nproto->handle_error(skb, error_info); } } return 0; }
static int ip6_rcv(struct sk_buff *skb, struct net_device *dev) { ip6hdr_t *ip6h = ip6_hdr(skb); const struct net_proto *nproto; if (ip6h->version != 6) { dev->stats.rx_err++; skb_free(skb); return 0; /* error: invalid hdr */ } if (skb->dev->hdr_len + IP6_HEADER_SIZE + ntohs(ip6h->payload_len) > skb->len) { dev->stats.rx_length_errors++; skb_free(skb); return 0; /* error: invalid length */ } /* Check recipiant */ assert(skb->dev != NULL); assert(inetdev_get_by_dev(skb->dev) != NULL); if (0 != memcmp(&inetdev_get_by_dev(skb->dev)->ifa6_address, &skb->nh.ip6h->daddr, sizeof(struct in6_addr))) { // skb_free(skb); // return 0; /* error: not for us */ } /* Setup transport layer header */ skb->h.raw = skb->nh.raw + IP6_HEADER_SIZE; nproto = net_proto_lookup(ETH_P_IPV6, ip6h->nexthdr); if (nproto != NULL) { return nproto->handle(skb); } // printk("ipv6 packet accepted, %#x\n", ip6h->nexthdr); skb_free(skb); return 0; /* error: nobody wants this packet */ }
static int ip_rcv(struct sk_buff *skb, struct net_device *dev) { net_device_stats_t *stats = &dev->stats; const struct net_proto *nproto; iphdr_t *iph = ip_hdr(skb); __u16 old_check; size_t ip_len; int optlen; sk_buff_t *complete_skb; /** * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. * Is the datagram acceptable? * 1. Length at least the size of an ip header * 2. Version of 4 * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] * 4. Doesn't have a bogus length */ if (skb->len < dev->hdr_len + IP_MIN_HEADER_SIZE || IP_HEADER_SIZE(iph) < IP_MIN_HEADER_SIZE || skb->len < dev->hdr_len + IP_HEADER_SIZE(iph)) { DBG(printk("ip_rcv: invalid IPv4 header length\n")); stats->rx_length_errors++; skb_free(skb); return 0; /* error: invalid header length */ } if (iph->version != 4) { DBG(printk("ip_rcv: invalid IPv4 version\n")); stats->rx_err++; skb_free(skb); return 0; /* error: not ipv4 */ } old_check = iph->check; ip_set_check_field(iph); if (old_check != iph->check) { DBG(printk("ip_rcv: invalid checksum %hx(%hx)\n", ntohs(old_check), ntohs(iph->check))); stats->rx_crc_errors++; skb_free(skb); return 0; /* error: invalid crc */ } ip_len = ntohs(iph->tot_len); if (ip_len < IP_HEADER_SIZE(iph) || skb->len < dev->hdr_len + ip_len) { DBG(printk("ip_rcv: invalid IPv4 length\n")); stats->rx_length_errors++; skb_free(skb); return 0; /* error: invalid length */ } /* Setup transport layer (L4) header */ skb->h.raw = skb->nh.raw + IP_HEADER_SIZE(iph); /* Validating */ if (0 != nf_test_skb(NF_CHAIN_INPUT, NF_TARGET_ACCEPT, skb)) { DBG(printk("ip_rcv: dropped by input netfilter\n")); stats->rx_dropped++; skb_free(skb); return 0; /* error: dropped */ } /* Forwarding */ assert(skb->dev); assert(inetdev_get_by_dev(skb->dev)); if (inetdev_get_by_dev(skb->dev)->ifa_address != 0) { /** * FIXME * This check needed for BOOTP protocol * disable forwarding if interface is not set yet */ /** * Check the destination address, and if it doesn't match * any of own addresses, retransmit packet according to the routing table. */ if (!ip_is_local(iph->daddr, IP_LOCAL_BROADCAST)) { if (0 != nf_test_skb(NF_CHAIN_FORWARD, NF_TARGET_ACCEPT, skb)) { DBG(printk("ip_rcv: dropped by forward netfilter\n")); stats->rx_dropped++; skb_free(skb); return 0; /* error: dropped */ } return ip_forward(skb); } } memset(skb->cb, 0, sizeof(skb->cb)); optlen = IP_HEADER_SIZE(iph) - IP_MIN_HEADER_SIZE; if (optlen > 0) { /* NOTE : maybe it'd be better to copy skb here, * 'cause options may cause modifications * but smart people who wrote linux kernel * say that this is extremely rarely needed */ ip_options_t *opts = (ip_options_t*)(skb->cb); memset(skb->cb, 0, sizeof(skb->cb)); opts->optlen = optlen; if (ip_options_compile(skb, opts)) { DBG(printk("ip_rcv: invalid options\n")); stats->rx_err++; skb_free(skb); return 0; /* error: bad ops */ } if (ip_options_handle_srr(skb)) { DBG(printk("ip_rcv: can't handle options\n")); stats->tx_err++; skb_free(skb); return 0; /* error: can't handle ops */ } } /* It's very useful for us to have complete packet even for forwarding * (we may apply any filter, we may perform NAT etc), * but it'll break routing if different parts of a fragmented * packet will use different routes. So they can't be assembled. * See RFC 1812 for details */ if (ntohs(skb->nh.iph->frag_off) & (IP_MF | IP_OFFSET)) { if ((complete_skb = ip_defrag(skb)) == NULL) { if (skb == NULL) { return 0; /* error: */ } return 0; } else { skb = complete_skb; iph = ip_hdr(complete_skb); } } /* When a packet is received, it is passed to any raw sockets * which have been bound to its protocol or to socket with concrete protocol */ raw_rcv(skb); nproto = net_proto_lookup(ETH_P_IP, iph->proto); if (nproto != NULL) { return nproto->handle(skb); } DBG(printk("ip_rcv: unknown protocol\n")); skb_free(skb); return 0; /* error: nobody wants this packet */ }