embox
1/**
2* @file
3* @brief The Internet Protocol (IP) module.
4*
5* @date 17.03.09
6* @author Alexandr Batyukov
7* @author Nikolay Korotky
8* @author Vladimir Sokolov
9* @author Ilia Vaprol
10*/
11
12#include <arpa/inet.h>13#include <string.h>14#include <errno.h>15
16#include <util/log.h>17
18#include <net/l3/ipv4/ip.h>19#include <net/l3/ipv4/ip_options.h>20#include <net/l3/icmpv4.h>21#include <net/l4/udp.h>22#include <net/socket/raw.h>23#include <net/inetdevice.h>24#include <net/l3/route.h>25#include <net/l3/ipv4/ip_fragment.h>26#include <net/netfilter.h>27#include <net/l2/ethernet.h>28#include <net/lib/ipv4.h>29
30#include <embox/net/proto.h>31#include <embox/net/pack.h>32
33EMBOX_NET_PACK(ETH_P_IP, ip_rcv);34
35static int ip_rcv(struct sk_buff *skb, struct net_device *dev) {36net_device_stats_t *stats = &dev->stats;37const struct net_proto *nproto;38iphdr_t *iph = ip_hdr(skb);39__u16 old_check;40size_t ip_len;41int optlen;42sk_buff_t *complete_skb;43
44/**45* RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum.
46* Is the datagram acceptable?
47* 1. Length at least the size of an ip header
48* 2. Version of 4
49* 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums]
50* 4. Doesn't have a bogus length
51*/
52if (skb->len < dev->hdr_len + IP_MIN_HEADER_SIZE53|| IP_HEADER_SIZE(iph) < IP_MIN_HEADER_SIZE54|| skb->len < dev->hdr_len + IP_HEADER_SIZE(iph)) {55log_debug("ip_rcv: invalid IPv4 header length");56stats->rx_length_errors++;57skb_free(skb);58return 0; /* error: invalid header length */59}60
61
62if (iph->version != 4) {63log_debug("ip_rcv: invalid IPv4 version");64stats->rx_err++;65skb_free(skb);66return 0; /* error: not ipv4 */67}68
69old_check = iph->check;70ip_set_check_field(iph);71if (old_check != iph->check) {72log_debug("ip_rcv: invalid checksum %hx(%hx)",73ntohs(old_check), ntohs(iph->check));74stats->rx_crc_errors++;75skb_free(skb);76return 0; /* error: invalid crc */77}78
79ip_len = ntohs(iph->tot_len);80if (ip_len < IP_HEADER_SIZE(iph)81|| skb->len < dev->hdr_len + ip_len) {82log_debug("ip_rcv: invalid IPv4 length");83stats->rx_length_errors++;84skb_free(skb);85return 0; /* error: invalid length */86}87
88/* Setup transport layer (L4) header */89skb->h.raw = skb->nh.raw + IP_HEADER_SIZE(iph);90
91/* Validating */92if (0 != nf_test_skb(NF_CHAIN_INPUT, NF_TARGET_ACCEPT, skb)) {93log_debug("ip_rcv: dropped by input netfilter");94stats->rx_dropped++;95skb_free(skb);96return 0; /* error: dropped */97}98
99/* Forwarding */100assert(skb->dev);101if (!inetdev_get_by_dev(skb->dev)) {102log_debug("ip_rcv: dropped by input because inet_dev is not set");103skb_free(skb);104return 0; /* didn't set inet dev yet */105}106
107if (inetdev_get_by_dev(skb->dev)->ifa_address != 0) {108/**109* FIXME
110* This check needed for BOOTP protocol
111* disable forwarding if interface is not set yet
112*/
113/**114* Check the destination address, and if it doesn't match
115* any of own addresses, retransmit packet according to the routing table.
116*/
117#if defined(NET_NAMESPACE_ENABLED) && (NET_NAMESPACE_ENABLED == 1)118if (!ip_is_local_net_ns(iph->daddr, IP_LOCAL_BROADCAST,119skb->dev->net_ns)) {120#else121if (!ip_is_local(iph->daddr, IP_LOCAL_BROADCAST)) {122#endif123if (0 != nf_test_skb(NF_CHAIN_FORWARD, NF_TARGET_ACCEPT, skb)) {124log_debug("ip_rcv: dropped by forward netfilter");125stats->rx_dropped++;126skb_free(skb);127return 0; /* error: dropped */128}129return ip_forward(skb);130}131}132
133memset(skb->cb, 0, sizeof(skb->cb));134optlen = IP_HEADER_SIZE(iph) - IP_MIN_HEADER_SIZE;135if (optlen > 0) {136/* NOTE : maybe it'd be better to copy skb here,137* 'cause options may cause modifications
138* but smart people who wrote linux kernel
139* say that this is extremely rarely needed
140*/
141ip_options_t *opts = (ip_options_t*)(skb->cb);142
143memset(skb->cb, 0, sizeof(skb->cb));144opts->optlen = optlen;145if (ip_options_compile(skb, opts)) {146log_debug("ip_rcv: invalid options");147stats->rx_err++;148skb_free(skb);149return 0; /* error: bad ops */150}151if (ip_options_handle_srr(skb)) {152log_debug("ip_rcv: can't handle options");153stats->tx_err++;154skb_free(skb);155return 0; /* error: can't handle ops */156}157}158
159/* It's very useful for us to have complete packet even for forwarding160* (we may apply any filter, we may perform NAT etc),
161* but it'll break routing if different parts of a fragmented
162* packet will use different routes. So they can't be assembled.
163* See RFC 1812 for details
164*/
165if (ntohs(skb->nh.iph->frag_off) & (IP_MF | IP_OFFSET)) {166if ((complete_skb = ip_defrag(skb)) == NULL) {167return 0;168} else {169skb = complete_skb;170iph = ip_hdr(complete_skb);171}172}173
174/* When a packet is received, it is passed to any raw sockets175* which have been bound to its protocol or to socket with concrete protocol */
176raw_rcv(skb);177
178nproto = net_proto_lookup(ETH_P_IP, iph->proto);179if (nproto != NULL) {180return nproto->handle(skb);181}182
183log_debug("ip_rcv: unknown protocol %d", iph->proto);184skb_free(skb);185return 0; /* error: nobody wants this packet */186}
187