int ip_direction_indexer(const void *vp) { const ip_message *ip = vp; if (ip_is_local(&ip->src)) return 0; if (ip_is_local(&ip->dst)) return 1; return LARGEST; }
int ip_direction_indexer(const void *vp) { const dns_message *m = vp; const transport_message *tm = m->tm; if (ip_is_local(&tm->src_ip_addr)) return 0; if (ip_is_local(&tm->dst_ip_addr)) return 1; return LARGEST; }
/* Add a TCP relay associated to the friend. * * return -1 on failure. * return 0 on success. */ int friend_add_tcp_relay(Friend_Connections *fr_c, int friendcon_id, IP_Port ip_port, const uint8_t *public_key) { Friend_Conn *const friend_con = get_conn(fr_c, friendcon_id); if (!friend_con) { return -1; } /* Local ip and same pk means that they are hosting a TCP relay. */ if (ip_is_local(ip_port.ip) && public_key_cmp(friend_con->dht_temp_pk, public_key) == 0) { if (!net_family_is_unspec(friend_con->dht_ip_port.ip.family)) { ip_port.ip = friend_con->dht_ip_port.ip; } else { friend_con->hosting_tcp_relay = 0; } } const uint16_t index = friend_con->tcp_relay_counter % FRIEND_MAX_STORED_TCP_RELAYS; for (unsigned i = 0; i < FRIEND_MAX_STORED_TCP_RELAYS; ++i) { if (!net_family_is_unspec(friend_con->tcp_relays[i].ip_port.ip.family) && public_key_cmp(friend_con->tcp_relays[i].public_key, public_key) == 0) { memset(&friend_con->tcp_relays[i], 0, sizeof(Node_format)); } } friend_con->tcp_relays[index].ip_port = ip_port; memcpy(friend_con->tcp_relays[index].public_key, public_key, CRYPTO_PUBLIC_KEY_SIZE); ++friend_con->tcp_relay_counter; return add_tcp_relay_peer(fr_c->net_crypto, friend_con->crypt_connection_id, ip_port, public_key); }
/* Is IP a local ip or not. */ bool ip_is_local(IP ip) { if (ip.family == TOX_AF_INET) { IP4 ip4 = ip.ip.v4; /* Loopback. */ if (ip4.uint8[0] == 127) { return 1; } } else { /* embedded IPv4-in-IPv6 */ if (IPV6_IPV4_IN_V6(ip.ip.v6)) { IP ip4; ip4.family = TOX_AF_INET; ip4.ip.v4.uint32 = ip.ip.v6.uint32[3]; return ip_is_local(ip4); } /* localhost in IPv6 (::1) */ if (ip.ip.v6.uint64[0] == 0 && ip.ip.v6.uint32[2] == 0 && ip.ip.v6.uint32[3] == net_htonl(1)) { return 1; } } return 0; }
/* return 0 if ip is a LAN ip. * return -1 if it is not. */ int ip_is_lan(IP ip) { if (ip_is_local(ip)) { return 0; } if (ip.family == TOX_AF_INET) { IP4 ip4 = ip.ip.v4; /* 10.0.0.0 to 10.255.255.255 range. */ if (ip4.uint8[0] == 10) { return 0; } /* 172.16.0.0 to 172.31.255.255 range. */ if (ip4.uint8[0] == 172 && ip4.uint8[1] >= 16 && ip4.uint8[1] <= 31) { return 0; } /* 192.168.0.0 to 192.168.255.255 range. */ if (ip4.uint8[0] == 192 && ip4.uint8[1] == 168) { return 0; } /* 169.254.1.0 to 169.254.254.255 range. */ if (ip4.uint8[0] == 169 && ip4.uint8[1] == 254 && ip4.uint8[2] != 0 && ip4.uint8[2] != 255) { return 0; } /* RFC 6598: 100.64.0.0 to 100.127.255.255 (100.64.0.0/10) * (shared address space to stack another layer of NAT) */ if ((ip4.uint8[0] == 100) && ((ip4.uint8[1] & 0xC0) == 0x40)) { return 0; } } else if (ip.family == TOX_AF_INET6) { /* autogenerated for each interface: FE80::* (up to FEBF::*) FF02::1 is - according to RFC 4291 - multicast all-nodes link-local */ if (((ip.ip.v6.uint8[0] == 0xFF) && (ip.ip.v6.uint8[1] < 3) && (ip.ip.v6.uint8[15] == 1)) || ((ip.ip.v6.uint8[0] == 0xFE) && ((ip.ip.v6.uint8[1] & 0xC0) == 0x80))) { return 0; } /* embedded IPv4-in-IPv6 */ if (IPV6_IPV4_IN_V6(ip.ip.v6)) { IP ip4; ip4.family = TOX_AF_INET; ip4.ip.v4.uint32 = ip.ip.v6.uint32[3]; return ip_is_lan(ip4); } } return -1; }
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 */ }
int icmp_discard(struct sk_buff *skb, uint8_t type, uint8_t code, ...) { struct { union { struct icmpbody_dest_unreach dest_unreach; struct icmpbody_source_quench source_quench; struct icmpbody_redirect redirect; struct icmpbody_time_exceed time_exceed; struct icmpbody_param_prob param_prob; } __attribute__((packed)); char __body_msg_storage[ICMP_DISCARD_MAX_SIZE]; } __attribute__((packed)) body; va_list extra; uint8_t *body_msg; size_t body_msg_sz; if (!(ip_is_local(ip_hdr(skb)->saddr, 0) || ip_is_local(ip_hdr(skb)->daddr, 0)) || (ip_hdr(skb)->frag_off & htons(IP_OFFSET)) || (ip_data_length(ip_hdr(skb)) < ICMP_DISCARD_MIN_SIZE) || (ip_hdr(skb)->proto != IPPROTO_ICMP) || (skb->h.raw = skb->nh.raw + IP_HEADER_SIZE(ip_hdr(skb)), ICMP_TYPE_ERROR(icmp_hdr(skb)->type))) { skb_free(skb); return 0; /* error: inappropriate packet */ } switch (type) { default: assertf(0, "bad type for discard"); body_msg = (uint8_t *)&body.__body_msg_storage[0]; break; /* error: bad type for discard */ case ICMP_DEST_UNREACH: assertf(code < __ICMP_DEST_UNREACH_MAX, "incorrect code for type"); va_start(extra, code); body.dest_unreach.zero = 0; body.dest_unreach.mtu = code != ICMP_FRAG_NEEDED ? 0 : htons((uint16_t)va_arg(extra, int)); va_end(extra); body_msg = &body.dest_unreach.msg[0]; break; case ICMP_SOURCE_QUENCH: assertf(code == 0, "incorrect code for type"); body.source_quench.zero = 0; body_msg = &body.source_quench.msg[0]; break; case ICMP_REDIRECT: assertf(code < __ICMP_REDIRECT_MAX, "incorrect code for type"); va_start(extra, code); memcpy(&body.redirect.gateway, va_arg(extra, struct in_addr *), sizeof body.redirect.gateway); va_end(extra); body_msg = &body.redirect.msg[0]; break; case ICMP_TIME_EXCEED: assertf(code < __ICMP_TIME_EXCEED_MAX, "incorrect code for type"); body.time_exceed.zero = 0; body_msg = &body.time_exceed.msg[0]; break; case ICMP_PARAM_PROB: assertf(code < __ICMP_PARAM_PROB_MAX, "incorrect code for type"); va_start(extra, code); body.param_prob.ptr = code != ICMP_PTR_ERROR ? 0 : (uint8_t)va_arg(extra, int); body.param_prob.zero1 = body.param_prob.zero2 = 0; va_end(extra); body_msg = &body.param_prob.msg[0]; break; } body_msg_sz = min(ip_data_length(ip_hdr(skb)), sizeof body.__body_msg_storage); memcpy(body_msg, ip_hdr(skb), body_msg_sz); if (NULL == skb_declone(skb)) { skb_free(skb); return -ENOMEM; /* error: can't declone data */ } return icmp_send(type, code, &body, sizeof body - sizeof body.__body_msg_storage + body_msg_sz, skb); }