1147231Sglebius// SPDX-License-Identifier: GPL-2.0-only 2147231Sglebius/* 3147231Sglebius * Transparent proxy support for Linux/iptables 4147231Sglebius * 5147231Sglebius * Copyright (C) 2007-2008 BalaBit IT Ltd. 6147231Sglebius * Author: Krisztian Kovacs 7147231Sglebius */ 8147231Sglebius#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 9147231Sglebius#include <linux/module.h> 10147231Sglebius#include <linux/skbuff.h> 11147231Sglebius#include <linux/netfilter/x_tables.h> 12147231Sglebius#include <linux/netfilter_ipv4/ip_tables.h> 13147231Sglebius#include <net/tcp.h> 14147231Sglebius#include <net/udp.h> 15147231Sglebius#include <net/icmp.h> 16147231Sglebius#include <net/sock.h> 17147231Sglebius#include <net/inet_sock.h> 18147231Sglebius#include <net/netfilter/ipv4/nf_defrag_ipv4.h> 19147231Sglebius 20147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 21147231Sglebius#include <linux/netfilter_ipv6/ip6_tables.h> 22147231Sglebius#include <net/inet6_hashtables.h> 23147231Sglebius#include <net/netfilter/ipv6/nf_defrag_ipv6.h> 24147231Sglebius#endif 25147231Sglebius 26147231Sglebius#include <net/netfilter/nf_socket.h> 27147231Sglebius#include <linux/netfilter/xt_socket.h> 28147231Sglebius 29147231Sglebius/* "socket" match based redirection (no specific rule) 30147231Sglebius * =================================================== 31147231Sglebius * 32147231Sglebius * There are connections with dynamic endpoints (e.g. FTP data 33147231Sglebius * connection) that the user is unable to add explicit rules 34147231Sglebius * for. These are taken care of by a generic "socket" rule. It is 35147231Sglebius * assumed that the proxy application is trusted to open such 36147231Sglebius * connections without explicit iptables rule (except of course the 37147231Sglebius * generic 'socket' rule). In this case the following sockets are 38147231Sglebius * matched in preference order: 39147231Sglebius * 40147231Sglebius * - match: if there's a fully established connection matching the 41147231Sglebius * _packet_ tuple 42147231Sglebius * 43147231Sglebius * - match: if there's a non-zero bound listener (possibly with a 44147231Sglebius * non-local address) We don't accept zero-bound listeners, since 45147231Sglebius * then local services could intercept traffic going through the 46147231Sglebius * box. 47147231Sglebius */ 48147231Sglebiusstatic bool 49147231Sglebiussocket_match(const struct sk_buff *skb, struct xt_action_param *par, 50206032Smav const struct xt_socket_mtinfo1 *info) 51147231Sglebius{ 52147231Sglebius struct sk_buff *pskb = (struct sk_buff *)skb; 53147231Sglebius struct sock *sk = skb->sk; 54147231Sglebius 55147231Sglebius if (sk && !net_eq(xt_net(par), sock_net(sk))) 56147231Sglebius sk = NULL; 57147231Sglebius 58147231Sglebius if (!sk) 59147231Sglebius sk = nf_sk_lookup_slow_v4(xt_net(par), skb, xt_in(par)); 60147231Sglebius 61147231Sglebius if (sk) { 62147231Sglebius bool wildcard; 63147231Sglebius bool transparent = true; 64147231Sglebius 65147231Sglebius /* Ignore sockets listening on INADDR_ANY, 66147231Sglebius * unless XT_SOCKET_NOWILDCARD is set 67147231Sglebius */ 68147231Sglebius wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) && 69147231Sglebius sk_fullsock(sk) && 70147231Sglebius inet_sk(sk)->inet_rcv_saddr == 0); 71147231Sglebius 72147231Sglebius /* Ignore non-transparent sockets, 73147231Sglebius * if XT_SOCKET_TRANSPARENT is used 74147231Sglebius */ 75147231Sglebius if (info->flags & XT_SOCKET_TRANSPARENT) 76147231Sglebius transparent = inet_sk_transparent(sk); 77147231Sglebius 78147231Sglebius if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && 79147231Sglebius transparent && sk_fullsock(sk)) 80147231Sglebius pskb->mark = READ_ONCE(sk->sk_mark); 81147231Sglebius 82147231Sglebius if (sk != skb->sk) 83147231Sglebius sock_gen_put(sk); 84147231Sglebius 85147231Sglebius if (wildcard || !transparent) 86147231Sglebius sk = NULL; 87147231Sglebius } 88147231Sglebius 89147231Sglebius return sk != NULL; 90147231Sglebius} 91147231Sglebius 92147231Sglebiusstatic bool 93147231Sglebiussocket_mt4_v0(const struct sk_buff *skb, struct xt_action_param *par) 94147231Sglebius{ 95147231Sglebius static struct xt_socket_mtinfo1 xt_info_v0 = { 96147231Sglebius .flags = 0, 97147231Sglebius }; 98147231Sglebius 99147231Sglebius return socket_match(skb, par, &xt_info_v0); 100147231Sglebius} 101147231Sglebius 102147231Sglebiusstatic bool 103147231Sglebiussocket_mt4_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) 104147231Sglebius{ 105147231Sglebius return socket_match(skb, par, par->matchinfo); 106147231Sglebius} 107147231Sglebius 108147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 109147231Sglebiusstatic bool 110147231Sglebiussocket_mt6_v1_v2_v3(const struct sk_buff *skb, struct xt_action_param *par) 111147231Sglebius{ 112147231Sglebius const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; 113147231Sglebius struct sk_buff *pskb = (struct sk_buff *)skb; 114147231Sglebius struct sock *sk = skb->sk; 115147231Sglebius 116147231Sglebius if (sk && !net_eq(xt_net(par), sock_net(sk))) 117147231Sglebius sk = NULL; 118147231Sglebius 119147231Sglebius if (!sk) 120147231Sglebius sk = nf_sk_lookup_slow_v6(xt_net(par), skb, xt_in(par)); 121147231Sglebius 122147231Sglebius if (sk) { 123147231Sglebius bool wildcard; 124147231Sglebius bool transparent = true; 125147231Sglebius 126147231Sglebius /* Ignore sockets listening on INADDR_ANY 127147231Sglebius * unless XT_SOCKET_NOWILDCARD is set 128147231Sglebius */ 129147231Sglebius wildcard = (!(info->flags & XT_SOCKET_NOWILDCARD) && 130147231Sglebius sk_fullsock(sk) && 131147231Sglebius ipv6_addr_any(&sk->sk_v6_rcv_saddr)); 132147231Sglebius 133147231Sglebius /* Ignore non-transparent sockets, 134147231Sglebius * if XT_SOCKET_TRANSPARENT is used 135147231Sglebius */ 136147231Sglebius if (info->flags & XT_SOCKET_TRANSPARENT) 137147231Sglebius transparent = inet_sk_transparent(sk); 138147231Sglebius 139147231Sglebius if (info->flags & XT_SOCKET_RESTORESKMARK && !wildcard && 140147231Sglebius transparent && sk_fullsock(sk)) 141147231Sglebius pskb->mark = READ_ONCE(sk->sk_mark); 142147231Sglebius 143147231Sglebius if (sk != skb->sk) 144147231Sglebius sock_gen_put(sk); 145147231Sglebius 146147231Sglebius if (wildcard || !transparent) 147147231Sglebius sk = NULL; 148147231Sglebius } 149147231Sglebius 150147231Sglebius return sk != NULL; 151147231Sglebius} 152147231Sglebius#endif 153147231Sglebius 154147231Sglebiusstatic int socket_mt_enable_defrag(struct net *net, int family) 155147231Sglebius{ 156147231Sglebius switch (family) { 157147231Sglebius case NFPROTO_IPV4: 158147231Sglebius return nf_defrag_ipv4_enable(net); 159147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 160147231Sglebius case NFPROTO_IPV6: 161147231Sglebius return nf_defrag_ipv6_enable(net); 162147231Sglebius#endif 163184205Sdes } 164147231Sglebius WARN_ONCE(1, "Unknown family %d\n", family); 165147231Sglebius return 0; 166147231Sglebius} 167147231Sglebius 168147231Sglebiusstatic int socket_mt_v1_check(const struct xt_mtchk_param *par) 169147231Sglebius{ 170147231Sglebius const struct xt_socket_mtinfo1 *info = (struct xt_socket_mtinfo1 *) par->matchinfo; 171147231Sglebius int err; 172147231Sglebius 173147231Sglebius err = socket_mt_enable_defrag(par->net, par->family); 174147231Sglebius if (err) 175147231Sglebius return err; 176147231Sglebius 177147231Sglebius if (info->flags & ~XT_SOCKET_FLAGS_V1) { 178147231Sglebius pr_info_ratelimited("unknown flags 0x%x\n", 179147231Sglebius info->flags & ~XT_SOCKET_FLAGS_V1); 180147231Sglebius return -EINVAL; 181147231Sglebius } 182147231Sglebius return 0; 183147231Sglebius} 184147231Sglebius 185147231Sglebiusstatic int socket_mt_v2_check(const struct xt_mtchk_param *par) 186147231Sglebius{ 187147231Sglebius const struct xt_socket_mtinfo2 *info = (struct xt_socket_mtinfo2 *) par->matchinfo; 188147231Sglebius int err; 189147231Sglebius 190147231Sglebius err = socket_mt_enable_defrag(par->net, par->family); 191147231Sglebius if (err) 192147231Sglebius return err; 193147231Sglebius 194147231Sglebius if (info->flags & ~XT_SOCKET_FLAGS_V2) { 195147231Sglebius pr_info_ratelimited("unknown flags 0x%x\n", 196147231Sglebius info->flags & ~XT_SOCKET_FLAGS_V2); 197147231Sglebius return -EINVAL; 198147231Sglebius } 199147231Sglebius return 0; 200147231Sglebius} 201147231Sglebius 202147231Sglebiusstatic int socket_mt_v3_check(const struct xt_mtchk_param *par) 203147231Sglebius{ 204147231Sglebius const struct xt_socket_mtinfo3 *info = 205147231Sglebius (struct xt_socket_mtinfo3 *)par->matchinfo; 206147231Sglebius int err; 207147231Sglebius 208147231Sglebius err = socket_mt_enable_defrag(par->net, par->family); 209147231Sglebius if (err) 210147231Sglebius return err; 211147231Sglebius if (info->flags & ~XT_SOCKET_FLAGS_V3) { 212147231Sglebius pr_info_ratelimited("unknown flags 0x%x\n", 213147231Sglebius info->flags & ~XT_SOCKET_FLAGS_V3); 214147231Sglebius return -EINVAL; 215147231Sglebius } 216147231Sglebius return 0; 217147231Sglebius} 218147231Sglebius 219147231Sglebiusstatic void socket_mt_destroy(const struct xt_mtdtor_param *par) 220147231Sglebius{ 221147231Sglebius if (par->family == NFPROTO_IPV4) 222147231Sglebius nf_defrag_ipv4_disable(par->net); 223147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 224147231Sglebius else if (par->family == NFPROTO_IPV6) 225147231Sglebius nf_defrag_ipv6_disable(par->net); 226147231Sglebius#endif 227147231Sglebius} 228147231Sglebius 229147231Sglebiusstatic struct xt_match socket_mt_reg[] __read_mostly = { 230147231Sglebius { 231147231Sglebius .name = "socket", 232147231Sglebius .revision = 0, 233147231Sglebius .family = NFPROTO_IPV4, 234147231Sglebius .match = socket_mt4_v0, 235147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 236147231Sglebius (1 << NF_INET_LOCAL_IN), 237147231Sglebius .me = THIS_MODULE, 238147231Sglebius }, 239147231Sglebius { 240147231Sglebius .name = "socket", 241147231Sglebius .revision = 1, 242147231Sglebius .family = NFPROTO_IPV4, 243147231Sglebius .match = socket_mt4_v1_v2_v3, 244147231Sglebius .destroy = socket_mt_destroy, 245147231Sglebius .checkentry = socket_mt_v1_check, 246147231Sglebius .matchsize = sizeof(struct xt_socket_mtinfo1), 247147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 248147231Sglebius (1 << NF_INET_LOCAL_IN), 249147231Sglebius .me = THIS_MODULE, 250147231Sglebius }, 251147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 252147231Sglebius { 253147231Sglebius .name = "socket", 254147231Sglebius .revision = 1, 255147231Sglebius .family = NFPROTO_IPV6, 256147231Sglebius .match = socket_mt6_v1_v2_v3, 257147231Sglebius .checkentry = socket_mt_v1_check, 258147231Sglebius .matchsize = sizeof(struct xt_socket_mtinfo1), 259147231Sglebius .destroy = socket_mt_destroy, 260147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 261147231Sglebius (1 << NF_INET_LOCAL_IN), 262147231Sglebius .me = THIS_MODULE, 263147231Sglebius }, 264147231Sglebius#endif 265147231Sglebius { 266147231Sglebius .name = "socket", 267147231Sglebius .revision = 2, 268147231Sglebius .family = NFPROTO_IPV4, 269147231Sglebius .match = socket_mt4_v1_v2_v3, 270147231Sglebius .checkentry = socket_mt_v2_check, 271147231Sglebius .destroy = socket_mt_destroy, 272147231Sglebius .matchsize = sizeof(struct xt_socket_mtinfo1), 273147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 274147231Sglebius (1 << NF_INET_LOCAL_IN), 275147231Sglebius .me = THIS_MODULE, 276147231Sglebius }, 277147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 278147231Sglebius { 279147231Sglebius .name = "socket", 280147231Sglebius .revision = 2, 281147231Sglebius .family = NFPROTO_IPV6, 282147231Sglebius .match = socket_mt6_v1_v2_v3, 283147231Sglebius .checkentry = socket_mt_v2_check, 284147231Sglebius .destroy = socket_mt_destroy, 285147231Sglebius .matchsize = sizeof(struct xt_socket_mtinfo1), 286147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 287147231Sglebius (1 << NF_INET_LOCAL_IN), 288147231Sglebius .me = THIS_MODULE, 289147231Sglebius }, 290147231Sglebius#endif 291147231Sglebius { 292147231Sglebius .name = "socket", 293147248Sglebius .revision = 3, 294147231Sglebius .family = NFPROTO_IPV4, 295147248Sglebius .match = socket_mt4_v1_v2_v3, 296147248Sglebius .checkentry = socket_mt_v3_check, 297147231Sglebius .destroy = socket_mt_destroy, 298147231Sglebius .matchsize = sizeof(struct xt_socket_mtinfo1), 299147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 300147231Sglebius (1 << NF_INET_LOCAL_IN), 301147231Sglebius .me = THIS_MODULE, 302147231Sglebius }, 303147231Sglebius#if IS_ENABLED(CONFIG_IP6_NF_IPTABLES) 304147231Sglebius { 305147231Sglebius .name = "socket", 306147231Sglebius .revision = 3, 307147231Sglebius .family = NFPROTO_IPV6, 308147231Sglebius .match = socket_mt6_v1_v2_v3, 309147231Sglebius .checkentry = socket_mt_v3_check, 310147231Sglebius .destroy = socket_mt_destroy, 311147231Sglebius .matchsize = sizeof(struct xt_socket_mtinfo1), 312147231Sglebius .hooks = (1 << NF_INET_PRE_ROUTING) | 313147231Sglebius (1 << NF_INET_LOCAL_IN), 314147231Sglebius .me = THIS_MODULE, 315147231Sglebius }, 316147231Sglebius#endif 317147231Sglebius}; 318147248Sglebius 319166018Sglebiusstatic int __init socket_mt_init(void) 320147231Sglebius{ 321147231Sglebius return xt_register_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); 322147231Sglebius} 323147231Sglebius 324147231Sglebiusstatic void __exit socket_mt_exit(void) 325147231Sglebius{ 326147231Sglebius xt_unregister_matches(socket_mt_reg, ARRAY_SIZE(socket_mt_reg)); 327147231Sglebius} 328147231Sglebius 329147231Sglebiusmodule_init(socket_mt_init); 330147231Sglebiusmodule_exit(socket_mt_exit); 331147231Sglebius 332147231SglebiusMODULE_LICENSE("GPL"); 333147231SglebiusMODULE_AUTHOR("Krisztian Kovacs, Balazs Scheidler"); 334147248SglebiusMODULE_DESCRIPTION("x_tables socket match module"); 335166018SglebiusMODULE_ALIAS("ipt_socket"); 336166018SglebiusMODULE_ALIAS("ip6t_socket"); 337147231Sglebius