1 /* 2 * Copyright (C) 2007-2008 BalaBit IT Ltd. 3 * Author: Krisztian Kovacs 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 */ 10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 11 #include <linux/module.h> 12 #include <linux/skbuff.h> 13 #include <net/tcp.h> 14 #include <net/udp.h> 15 #include <net/icmp.h> 16 #include <net/sock.h> 17 #include <net/inet_sock.h> 18 #include <net/netfilter/nf_socket.h> 19 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 20 #include <net/netfilter/nf_conntrack.h> 21 #endif 22 23 static int 24 extract_icmp4_fields(const struct sk_buff *skb, u8 *protocol, 25 __be32 *raddr, __be32 *laddr, 26 __be16 *rport, __be16 *lport) 27 { 28 unsigned int outside_hdrlen = ip_hdrlen(skb); 29 struct iphdr *inside_iph, _inside_iph; 30 struct icmphdr *icmph, _icmph; 31 __be16 *ports, _ports[2]; 32 33 icmph = skb_header_pointer(skb, outside_hdrlen, 34 sizeof(_icmph), &_icmph); 35 if (icmph == NULL) 36 return 1; 37 38 switch (icmph->type) { 39 case ICMP_DEST_UNREACH: 40 case ICMP_SOURCE_QUENCH: 41 case ICMP_REDIRECT: 42 case ICMP_TIME_EXCEEDED: 43 case ICMP_PARAMETERPROB: 44 break; 45 default: 46 return 1; 47 } 48 49 inside_iph = skb_header_pointer(skb, outside_hdrlen + 50 sizeof(struct icmphdr), 51 sizeof(_inside_iph), &_inside_iph); 52 if (inside_iph == NULL) 53 return 1; 54 55 if (inside_iph->protocol != IPPROTO_TCP && 56 inside_iph->protocol != IPPROTO_UDP) 57 return 1; 58 59 ports = skb_header_pointer(skb, outside_hdrlen + 60 sizeof(struct icmphdr) + 61 (inside_iph->ihl << 2), 62 sizeof(_ports), &_ports); 63 if (ports == NULL) 64 return 1; 65 66 /* the inside IP packet is the one quoted from our side, thus 67 * its saddr is the local address */ 68 *protocol = inside_iph->protocol; 69 *laddr = inside_iph->saddr; 70 *lport = ports[0]; 71 *raddr = inside_iph->daddr; 72 *rport = ports[1]; 73 74 return 0; 75 } 76 77 static struct sock * 78 nf_socket_get_sock_v4(struct net *net, struct sk_buff *skb, const int doff, 79 const u8 protocol, 80 const __be32 saddr, const __be32 daddr, 81 const __be16 sport, const __be16 dport, 82 const struct net_device *in) 83 { 84 switch (protocol) { 85 case IPPROTO_TCP: 86 return inet_lookup(net, &tcp_hashinfo, skb, doff, 87 saddr, sport, daddr, dport, 88 in->ifindex); 89 case IPPROTO_UDP: 90 return udp4_lib_lookup(net, saddr, sport, daddr, dport, 91 in->ifindex); 92 } 93 return NULL; 94 } 95 96 struct sock *nf_sk_lookup_slow_v4(struct net *net, const struct sk_buff *skb, 97 const struct net_device *indev) 98 { 99 __be32 uninitialized_var(daddr), uninitialized_var(saddr); 100 __be16 uninitialized_var(dport), uninitialized_var(sport); 101 const struct iphdr *iph = ip_hdr(skb); 102 struct sk_buff *data_skb = NULL; 103 u8 uninitialized_var(protocol); 104 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 105 enum ip_conntrack_info ctinfo; 106 struct nf_conn const *ct; 107 #endif 108 int doff = 0; 109 110 if (iph->protocol == IPPROTO_UDP || iph->protocol == IPPROTO_TCP) { 111 struct udphdr _hdr, *hp; 112 113 hp = skb_header_pointer(skb, ip_hdrlen(skb), 114 sizeof(_hdr), &_hdr); 115 if (hp == NULL) 116 return NULL; 117 118 protocol = iph->protocol; 119 saddr = iph->saddr; 120 sport = hp->source; 121 daddr = iph->daddr; 122 dport = hp->dest; 123 data_skb = (struct sk_buff *)skb; 124 doff = iph->protocol == IPPROTO_TCP ? 125 ip_hdrlen(skb) + __tcp_hdrlen((struct tcphdr *)hp) : 126 ip_hdrlen(skb) + sizeof(*hp); 127 128 } else if (iph->protocol == IPPROTO_ICMP) { 129 if (extract_icmp4_fields(skb, &protocol, &saddr, &daddr, 130 &sport, &dport)) 131 return NULL; 132 } else { 133 return NULL; 134 } 135 136 #if IS_ENABLED(CONFIG_NF_CONNTRACK) 137 /* Do the lookup with the original socket address in 138 * case this is a reply packet of an established 139 * SNAT-ted connection. 140 */ 141 ct = nf_ct_get(skb, &ctinfo); 142 if (ct && 143 ((iph->protocol != IPPROTO_ICMP && 144 ctinfo == IP_CT_ESTABLISHED_REPLY) || 145 (iph->protocol == IPPROTO_ICMP && 146 ctinfo == IP_CT_RELATED_REPLY)) && 147 (ct->status & IPS_SRC_NAT_DONE)) { 148 149 daddr = ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip; 150 dport = (iph->protocol == IPPROTO_TCP) ? 151 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.tcp.port : 152 ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u.udp.port; 153 } 154 #endif 155 156 return nf_socket_get_sock_v4(net, data_skb, doff, protocol, saddr, 157 daddr, sport, dport, indev); 158 } 159 EXPORT_SYMBOL_GPL(nf_sk_lookup_slow_v4); 160 161 MODULE_LICENSE("GPL"); 162 MODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 163 MODULE_DESCRIPTION("Netfilter IPv4 socket lookup infrastructure"); 164