/*
|
/*
|
* This is a module which is used for rejecting packets.
|
* This is a module which is used for rejecting packets.
|
* Added support for customized reject packets (Jozsef Kadlecsik).
|
* Added support for customized reject packets (Jozsef Kadlecsik).
|
* Added support for ICMP type-3-code-13 (Maciej Soltysiak). [RFC 1812]
|
* Added support for ICMP type-3-code-13 (Maciej Soltysiak). [RFC 1812]
|
*/
|
*/
|
#include <linux/config.h>
|
#include <linux/config.h>
|
#include <linux/module.h>
|
#include <linux/module.h>
|
#include <linux/skbuff.h>
|
#include <linux/skbuff.h>
|
#include <linux/ip.h>
|
#include <linux/ip.h>
|
#include <linux/udp.h>
|
#include <linux/udp.h>
|
#include <linux/icmp.h>
|
#include <linux/icmp.h>
|
#include <net/icmp.h>
|
#include <net/icmp.h>
|
#include <net/ip.h>
|
#include <net/ip.h>
|
#include <net/tcp.h>
|
#include <net/tcp.h>
|
#include <net/route.h>
|
#include <net/route.h>
|
#include <linux/netfilter_ipv4/ip_tables.h>
|
#include <linux/netfilter_ipv4/ip_tables.h>
|
#include <linux/netfilter_ipv4/ipt_REJECT.h>
|
#include <linux/netfilter_ipv4/ipt_REJECT.h>
|
|
|
#if 0
|
#if 0
|
#define DEBUGP printk
|
#define DEBUGP printk
|
#else
|
#else
|
#define DEBUGP(format, args...)
|
#define DEBUGP(format, args...)
|
#endif
|
#endif
|
|
|
/* If the original packet is part of a connection, but the connection
|
/* If the original packet is part of a connection, but the connection
|
is not confirmed, our manufactured reply will not be associated
|
is not confirmed, our manufactured reply will not be associated
|
with it, so we need to do this manually. */
|
with it, so we need to do this manually. */
|
static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct)
|
static void connection_attach(struct sk_buff *new_skb, struct nf_ct_info *nfct)
|
{
|
{
|
void (*attach)(struct sk_buff *, struct nf_ct_info *);
|
void (*attach)(struct sk_buff *, struct nf_ct_info *);
|
|
|
/* Avoid module unload race with ip_ct_attach being NULLed out */
|
/* Avoid module unload race with ip_ct_attach being NULLed out */
|
if (nfct && (attach = ip_ct_attach) != NULL)
|
if (nfct && (attach = ip_ct_attach) != NULL)
|
attach(new_skb, nfct);
|
attach(new_skb, nfct);
|
}
|
}
|
|
|
static inline struct rtable *route_reverse(struct sk_buff *skb, int hook)
|
static inline struct rtable *route_reverse(struct sk_buff *skb, int hook)
|
{
|
{
|
struct iphdr *iph = skb->nh.iph;
|
struct iphdr *iph = skb->nh.iph;
|
struct dst_entry *odst;
|
struct dst_entry *odst;
|
struct rt_key key = {};
|
struct rt_key key = {};
|
struct rtable *rt;
|
struct rtable *rt;
|
|
|
if (hook != NF_IP_FORWARD) {
|
if (hook != NF_IP_FORWARD) {
|
key.dst = iph->saddr;
|
key.dst = iph->saddr;
|
if (hook == NF_IP_LOCAL_IN)
|
if (hook == NF_IP_LOCAL_IN)
|
key.src = iph->daddr;
|
key.src = iph->daddr;
|
key.tos = RT_TOS(iph->tos);
|
key.tos = RT_TOS(iph->tos);
|
|
|
if (ip_route_output_key(&rt, &key) != 0)
|
if (ip_route_output_key(&rt, &key) != 0)
|
return NULL;
|
return NULL;
|
} else {
|
} else {
|
/* non-local src, find valid iif to satisfy
|
/* non-local src, find valid iif to satisfy
|
* rp-filter when calling ip_route_input. */
|
* rp-filter when calling ip_route_input. */
|
key.dst = iph->daddr;
|
key.dst = iph->daddr;
|
if (ip_route_output_key(&rt, &key) != 0)
|
if (ip_route_output_key(&rt, &key) != 0)
|
return NULL;
|
return NULL;
|
|
|
odst = skb->dst;
|
odst = skb->dst;
|
if (ip_route_input(skb, iph->saddr, iph->daddr,
|
if (ip_route_input(skb, iph->saddr, iph->daddr,
|
RT_TOS(iph->tos), rt->u.dst.dev) != 0) {
|
RT_TOS(iph->tos), rt->u.dst.dev) != 0) {
|
dst_release(&rt->u.dst);
|
dst_release(&rt->u.dst);
|
return NULL;
|
return NULL;
|
}
|
}
|
dst_release(&rt->u.dst);
|
dst_release(&rt->u.dst);
|
rt = (struct rtable *)skb->dst;
|
rt = (struct rtable *)skb->dst;
|
skb->dst = odst;
|
skb->dst = odst;
|
}
|
}
|
|
|
if (rt->u.dst.error) {
|
if (rt->u.dst.error) {
|
dst_release(&rt->u.dst);
|
dst_release(&rt->u.dst);
|
rt = NULL;
|
rt = NULL;
|
}
|
}
|
|
|
return rt;
|
return rt;
|
}
|
}
|
|
|
/* Send RST reply */
|
/* Send RST reply */
|
static void send_reset(struct sk_buff *oldskb, int hook)
|
static void send_reset(struct sk_buff *oldskb, int hook)
|
{
|
{
|
struct sk_buff *nskb;
|
struct sk_buff *nskb;
|
struct tcphdr *otcph, *tcph;
|
struct tcphdr *otcph, *tcph;
|
struct rtable *rt;
|
struct rtable *rt;
|
unsigned int otcplen;
|
unsigned int otcplen;
|
u_int16_t tmp_port;
|
u_int16_t tmp_port;
|
u_int32_t tmp_addr;
|
u_int32_t tmp_addr;
|
int needs_ack;
|
int needs_ack;
|
int hh_len;
|
int hh_len;
|
|
|
/* IP header checks: fragment, too short. */
|
/* IP header checks: fragment, too short. */
|
if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
|
if (oldskb->nh.iph->frag_off & htons(IP_OFFSET)
|
|| oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
|
|| oldskb->len < (oldskb->nh.iph->ihl<<2) + sizeof(struct tcphdr))
|
return;
|
return;
|
|
|
otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
|
otcph = (struct tcphdr *)((u_int32_t*)oldskb->nh.iph + oldskb->nh.iph->ihl);
|
otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
|
otcplen = oldskb->len - oldskb->nh.iph->ihl*4;
|
|
|
/* No RST for RST. */
|
/* No RST for RST. */
|
if (otcph->rst)
|
if (otcph->rst)
|
return;
|
return;
|
|
|
/* Check checksum. */
|
/* Check checksum. */
|
if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
|
if (tcp_v4_check(otcph, otcplen, oldskb->nh.iph->saddr,
|
oldskb->nh.iph->daddr,
|
oldskb->nh.iph->daddr,
|
csum_partial((char *)otcph, otcplen, 0)) != 0)
|
csum_partial((char *)otcph, otcplen, 0)) != 0)
|
return;
|
return;
|
|
|
if ((rt = route_reverse(oldskb, hook)) == NULL)
|
if ((rt = route_reverse(oldskb, hook)) == NULL)
|
return;
|
return;
|
|
|
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
|
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
|
|
|
|
|
/* Copy skb (even if skb is about to be dropped, we can't just
|
/* Copy skb (even if skb is about to be dropped, we can't just
|
clone it because there may be other things, such as tcpdump,
|
clone it because there may be other things, such as tcpdump,
|
interested in it). We also need to expand headroom in case
|
interested in it). We also need to expand headroom in case
|
hh_len of incoming interface < hh_len of outgoing interface */
|
hh_len of incoming interface < hh_len of outgoing interface */
|
nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
|
nskb = skb_copy_expand(oldskb, hh_len, skb_tailroom(oldskb),
|
GFP_ATOMIC);
|
GFP_ATOMIC);
|
if (!nskb) {
|
if (!nskb) {
|
dst_release(&rt->u.dst);
|
dst_release(&rt->u.dst);
|
return;
|
return;
|
}
|
}
|
|
|
dst_release(nskb->dst);
|
dst_release(nskb->dst);
|
nskb->dst = &rt->u.dst;
|
nskb->dst = &rt->u.dst;
|
|
|
/* This packet will not be the same as the other: clear nf fields */
|
/* This packet will not be the same as the other: clear nf fields */
|
nf_conntrack_put(nskb->nfct);
|
nf_conntrack_put(nskb->nfct);
|
nskb->nfct = NULL;
|
nskb->nfct = NULL;
|
nskb->nfcache = 0;
|
nskb->nfcache = 0;
|
#ifdef CONFIG_NETFILTER_DEBUG
|
#ifdef CONFIG_NETFILTER_DEBUG
|
nskb->nf_debug = 0;
|
nskb->nf_debug = 0;
|
#endif
|
#endif
|
nskb->nfmark = 0;
|
nskb->nfmark = 0;
|
|
|
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
|
tcph = (struct tcphdr *)((u_int32_t*)nskb->nh.iph + nskb->nh.iph->ihl);
|
|
|
/* Swap source and dest */
|
/* Swap source and dest */
|
tmp_addr = nskb->nh.iph->saddr;
|
tmp_addr = nskb->nh.iph->saddr;
|
nskb->nh.iph->saddr = nskb->nh.iph->daddr;
|
nskb->nh.iph->saddr = nskb->nh.iph->daddr;
|
nskb->nh.iph->daddr = tmp_addr;
|
nskb->nh.iph->daddr = tmp_addr;
|
tmp_port = tcph->source;
|
tmp_port = tcph->source;
|
tcph->source = tcph->dest;
|
tcph->source = tcph->dest;
|
tcph->dest = tmp_port;
|
tcph->dest = tmp_port;
|
|
|
/* Truncate to length (no data) */
|
/* Truncate to length (no data) */
|
tcph->doff = sizeof(struct tcphdr)/4;
|
tcph->doff = sizeof(struct tcphdr)/4;
|
skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
|
skb_trim(nskb, nskb->nh.iph->ihl*4 + sizeof(struct tcphdr));
|
nskb->nh.iph->tot_len = htons(nskb->len);
|
nskb->nh.iph->tot_len = htons(nskb->len);
|
|
|
if (tcph->ack) {
|
if (tcph->ack) {
|
needs_ack = 0;
|
needs_ack = 0;
|
tcph->seq = otcph->ack_seq;
|
tcph->seq = otcph->ack_seq;
|
tcph->ack_seq = 0;
|
tcph->ack_seq = 0;
|
} else {
|
} else {
|
needs_ack = 1;
|
needs_ack = 1;
|
tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
|
tcph->ack_seq = htonl(ntohl(otcph->seq) + otcph->syn + otcph->fin
|
+ otcplen - (otcph->doff<<2));
|
+ otcplen - (otcph->doff<<2));
|
tcph->seq = 0;
|
tcph->seq = 0;
|
}
|
}
|
|
|
/* Reset flags */
|
/* Reset flags */
|
((u_int8_t *)tcph)[13] = 0;
|
((u_int8_t *)tcph)[13] = 0;
|
tcph->rst = 1;
|
tcph->rst = 1;
|
tcph->ack = needs_ack;
|
tcph->ack = needs_ack;
|
|
|
tcph->window = 0;
|
tcph->window = 0;
|
tcph->urg_ptr = 0;
|
tcph->urg_ptr = 0;
|
|
|
/* Adjust TCP checksum */
|
/* Adjust TCP checksum */
|
tcph->check = 0;
|
tcph->check = 0;
|
tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
|
tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr),
|
nskb->nh.iph->saddr,
|
nskb->nh.iph->saddr,
|
nskb->nh.iph->daddr,
|
nskb->nh.iph->daddr,
|
csum_partial((char *)tcph,
|
csum_partial((char *)tcph,
|
sizeof(struct tcphdr), 0));
|
sizeof(struct tcphdr), 0));
|
|
|
/* Adjust IP TTL, DF */
|
/* Adjust IP TTL, DF */
|
nskb->nh.iph->ttl = MAXTTL;
|
nskb->nh.iph->ttl = MAXTTL;
|
/* Set DF, id = 0 */
|
/* Set DF, id = 0 */
|
nskb->nh.iph->frag_off = htons(IP_DF);
|
nskb->nh.iph->frag_off = htons(IP_DF);
|
nskb->nh.iph->id = 0;
|
nskb->nh.iph->id = 0;
|
|
|
/* Adjust IP checksum */
|
/* Adjust IP checksum */
|
nskb->nh.iph->check = 0;
|
nskb->nh.iph->check = 0;
|
nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
|
nskb->nh.iph->check = ip_fast_csum((unsigned char *)nskb->nh.iph,
|
nskb->nh.iph->ihl);
|
nskb->nh.iph->ihl);
|
|
|
/* "Never happens" */
|
/* "Never happens" */
|
if (nskb->len > nskb->dst->pmtu)
|
if (nskb->len > nskb->dst->pmtu)
|
goto free_nskb;
|
goto free_nskb;
|
|
|
connection_attach(nskb, oldskb->nfct);
|
connection_attach(nskb, oldskb->nfct);
|
|
|
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
ip_finish_output);
|
ip_finish_output);
|
return;
|
return;
|
|
|
free_nskb:
|
free_nskb:
|
kfree_skb(nskb);
|
kfree_skb(nskb);
|
}
|
}
|
|
|
static void send_unreach(struct sk_buff *skb_in, int code)
|
static void send_unreach(struct sk_buff *skb_in, int code)
|
{
|
{
|
struct iphdr *iph;
|
struct iphdr *iph;
|
struct udphdr *udph;
|
struct udphdr *udph;
|
struct icmphdr *icmph;
|
struct icmphdr *icmph;
|
struct sk_buff *nskb;
|
struct sk_buff *nskb;
|
u32 saddr;
|
u32 saddr;
|
u8 tos;
|
u8 tos;
|
int hh_len, length;
|
int hh_len, length;
|
struct rtable *rt = (struct rtable*)skb_in->dst;
|
struct rtable *rt = (struct rtable*)skb_in->dst;
|
unsigned char *data;
|
unsigned char *data;
|
|
|
if (!rt)
|
if (!rt)
|
return;
|
return;
|
|
|
/* FIXME: Use sysctl number. --RR */
|
/* FIXME: Use sysctl number. --RR */
|
if (!xrlim_allow(&rt->u.dst, 1*HZ))
|
if (!xrlim_allow(&rt->u.dst, 1*HZ))
|
return;
|
return;
|
|
|
iph = skb_in->nh.iph;
|
iph = skb_in->nh.iph;
|
|
|
/* No replies to physical multicast/broadcast */
|
/* No replies to physical multicast/broadcast */
|
if (skb_in->pkt_type!=PACKET_HOST)
|
if (skb_in->pkt_type!=PACKET_HOST)
|
return;
|
return;
|
|
|
/* Now check at the protocol level */
|
/* Now check at the protocol level */
|
if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
|
if (rt->rt_flags&(RTCF_BROADCAST|RTCF_MULTICAST))
|
return;
|
return;
|
|
|
/* Only reply to fragment 0. */
|
/* Only reply to fragment 0. */
|
if (iph->frag_off&htons(IP_OFFSET))
|
if (iph->frag_off&htons(IP_OFFSET))
|
return;
|
return;
|
|
|
/* if UDP checksum is set, verify it's correct */
|
/* if UDP checksum is set, verify it's correct */
|
if (iph->protocol == IPPROTO_UDP
|
if (iph->protocol == IPPROTO_UDP
|
&& skb_in->tail-(u8*)iph >= sizeof(struct udphdr)) {
|
&& skb_in->tail-(u8*)iph >= sizeof(struct udphdr)) {
|
int datalen = skb_in->len - (iph->ihl<<2);
|
int datalen = skb_in->len - (iph->ihl<<2);
|
udph = (struct udphdr *)((char *)iph + (iph->ihl<<2));
|
udph = (struct udphdr *)((char *)iph + (iph->ihl<<2));
|
if (udph->check
|
if (udph->check
|
&& csum_tcpudp_magic(iph->saddr, iph->daddr,
|
&& csum_tcpudp_magic(iph->saddr, iph->daddr,
|
datalen, IPPROTO_UDP,
|
datalen, IPPROTO_UDP,
|
csum_partial((char *)udph, datalen,
|
csum_partial((char *)udph, datalen,
|
0)) != 0)
|
0)) != 0)
|
return;
|
return;
|
}
|
}
|
|
|
/* If we send an ICMP error to an ICMP error a mess would result.. */
|
/* If we send an ICMP error to an ICMP error a mess would result.. */
|
if (iph->protocol == IPPROTO_ICMP
|
if (iph->protocol == IPPROTO_ICMP
|
&& skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
|
&& skb_in->tail-(u8*)iph >= sizeof(struct icmphdr)) {
|
icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
|
icmph = (struct icmphdr *)((char *)iph + (iph->ihl<<2));
|
/* Between echo-reply (0) and timestamp (13),
|
/* Between echo-reply (0) and timestamp (13),
|
everything except echo-request (8) is an error.
|
everything except echo-request (8) is an error.
|
Also, anything greater than NR_ICMP_TYPES is
|
Also, anything greater than NR_ICMP_TYPES is
|
unknown, and hence should be treated as an error... */
|
unknown, and hence should be treated as an error... */
|
if ((icmph->type < ICMP_TIMESTAMP
|
if ((icmph->type < ICMP_TIMESTAMP
|
&& icmph->type != ICMP_ECHOREPLY
|
&& icmph->type != ICMP_ECHOREPLY
|
&& icmph->type != ICMP_ECHO)
|
&& icmph->type != ICMP_ECHO)
|
|| icmph->type > NR_ICMP_TYPES)
|
|| icmph->type > NR_ICMP_TYPES)
|
return;
|
return;
|
}
|
}
|
|
|
saddr = iph->daddr;
|
saddr = iph->daddr;
|
if (!(rt->rt_flags & RTCF_LOCAL))
|
if (!(rt->rt_flags & RTCF_LOCAL))
|
saddr = 0;
|
saddr = 0;
|
|
|
tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
|
tos = (iph->tos & IPTOS_TOS_MASK) | IPTOS_PREC_INTERNETCONTROL;
|
|
|
if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))
|
if (ip_route_output(&rt, iph->saddr, saddr, RT_TOS(tos), 0))
|
return;
|
return;
|
|
|
/* RFC says return as much as we can without exceeding 576 bytes. */
|
/* RFC says return as much as we can without exceeding 576 bytes. */
|
length = skb_in->len + sizeof(struct iphdr) + sizeof(struct icmphdr);
|
length = skb_in->len + sizeof(struct iphdr) + sizeof(struct icmphdr);
|
|
|
if (length > rt->u.dst.pmtu)
|
if (length > rt->u.dst.pmtu)
|
length = rt->u.dst.pmtu;
|
length = rt->u.dst.pmtu;
|
if (length > 576)
|
if (length > 576)
|
length = 576;
|
length = 576;
|
|
|
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
|
hh_len = (rt->u.dst.dev->hard_header_len + 15)&~15;
|
|
|
nskb = alloc_skb(hh_len+15+length, GFP_ATOMIC);
|
nskb = alloc_skb(hh_len+15+length, GFP_ATOMIC);
|
if (!nskb) {
|
if (!nskb) {
|
ip_rt_put(rt);
|
ip_rt_put(rt);
|
return;
|
return;
|
}
|
}
|
|
|
nskb->priority = 0;
|
nskb->priority = 0;
|
nskb->dst = &rt->u.dst;
|
nskb->dst = &rt->u.dst;
|
skb_reserve(nskb, hh_len);
|
skb_reserve(nskb, hh_len);
|
|
|
/* Set up IP header */
|
/* Set up IP header */
|
iph = nskb->nh.iph
|
iph = nskb->nh.iph
|
= (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
|
= (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
|
iph->version=4;
|
iph->version=4;
|
iph->ihl=5;
|
iph->ihl=5;
|
iph->tos=tos;
|
iph->tos=tos;
|
iph->tot_len = htons(length);
|
iph->tot_len = htons(length);
|
|
|
/* PMTU discovery never applies to ICMP packets. */
|
/* PMTU discovery never applies to ICMP packets. */
|
iph->frag_off = 0;
|
iph->frag_off = 0;
|
|
|
iph->ttl = MAXTTL;
|
iph->ttl = MAXTTL;
|
ip_select_ident(iph, &rt->u.dst, NULL);
|
ip_select_ident(iph, &rt->u.dst, NULL);
|
iph->protocol=IPPROTO_ICMP;
|
iph->protocol=IPPROTO_ICMP;
|
iph->saddr=rt->rt_src;
|
iph->saddr=rt->rt_src;
|
iph->daddr=rt->rt_dst;
|
iph->daddr=rt->rt_dst;
|
iph->check=0;
|
iph->check=0;
|
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
|
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
|
|
|
/* Set up ICMP header. */
|
/* Set up ICMP header. */
|
icmph = nskb->h.icmph
|
icmph = nskb->h.icmph
|
= (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
|
= (struct icmphdr *)skb_put(nskb, sizeof(struct icmphdr));
|
icmph->type = ICMP_DEST_UNREACH;
|
icmph->type = ICMP_DEST_UNREACH;
|
icmph->code = code;
|
icmph->code = code;
|
icmph->un.gateway = 0;
|
icmph->un.gateway = 0;
|
icmph->checksum = 0;
|
icmph->checksum = 0;
|
|
|
/* Copy as much of original packet as will fit */
|
/* Copy as much of original packet as will fit */
|
data = skb_put(nskb,
|
data = skb_put(nskb,
|
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
|
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
|
/* FIXME: won't work with nonlinear skbs --RR */
|
/* FIXME: won't work with nonlinear skbs --RR */
|
memcpy(data, skb_in->nh.iph,
|
memcpy(data, skb_in->nh.iph,
|
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
|
length - sizeof(struct iphdr) - sizeof(struct icmphdr));
|
icmph->checksum = ip_compute_csum((unsigned char *)icmph,
|
icmph->checksum = ip_compute_csum((unsigned char *)icmph,
|
length - sizeof(struct iphdr));
|
length - sizeof(struct iphdr));
|
|
|
connection_attach(nskb, skb_in->nfct);
|
connection_attach(nskb, skb_in->nfct);
|
|
|
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, nskb, NULL, nskb->dst->dev,
|
ip_finish_output);
|
ip_finish_output);
|
}
|
}
|
|
|
static unsigned int reject(struct sk_buff **pskb,
|
static unsigned int reject(struct sk_buff **pskb,
|
unsigned int hooknum,
|
unsigned int hooknum,
|
const struct net_device *in,
|
const struct net_device *in,
|
const struct net_device *out,
|
const struct net_device *out,
|
const void *targinfo,
|
const void *targinfo,
|
void *userinfo)
|
void *userinfo)
|
{
|
{
|
const struct ipt_reject_info *reject = targinfo;
|
const struct ipt_reject_info *reject = targinfo;
|
|
|
/* Our naive response construction doesn't deal with IP
|
/* Our naive response construction doesn't deal with IP
|
options, and probably shouldn't try. */
|
options, and probably shouldn't try. */
|
if ((*pskb)->nh.iph->ihl<<2 != sizeof(struct iphdr))
|
if ((*pskb)->nh.iph->ihl<<2 != sizeof(struct iphdr))
|
return NF_DROP;
|
return NF_DROP;
|
|
|
/* WARNING: This code causes reentry within iptables.
|
/* WARNING: This code causes reentry within iptables.
|
This means that the iptables jump stack is now crap. We
|
This means that the iptables jump stack is now crap. We
|
must return an absolute verdict. --RR */
|
must return an absolute verdict. --RR */
|
switch (reject->with) {
|
switch (reject->with) {
|
case IPT_ICMP_NET_UNREACHABLE:
|
case IPT_ICMP_NET_UNREACHABLE:
|
send_unreach(*pskb, ICMP_NET_UNREACH);
|
send_unreach(*pskb, ICMP_NET_UNREACH);
|
break;
|
break;
|
case IPT_ICMP_HOST_UNREACHABLE:
|
case IPT_ICMP_HOST_UNREACHABLE:
|
send_unreach(*pskb, ICMP_HOST_UNREACH);
|
send_unreach(*pskb, ICMP_HOST_UNREACH);
|
break;
|
break;
|
case IPT_ICMP_PROT_UNREACHABLE:
|
case IPT_ICMP_PROT_UNREACHABLE:
|
send_unreach(*pskb, ICMP_PROT_UNREACH);
|
send_unreach(*pskb, ICMP_PROT_UNREACH);
|
break;
|
break;
|
case IPT_ICMP_PORT_UNREACHABLE:
|
case IPT_ICMP_PORT_UNREACHABLE:
|
send_unreach(*pskb, ICMP_PORT_UNREACH);
|
send_unreach(*pskb, ICMP_PORT_UNREACH);
|
break;
|
break;
|
case IPT_ICMP_NET_PROHIBITED:
|
case IPT_ICMP_NET_PROHIBITED:
|
send_unreach(*pskb, ICMP_NET_ANO);
|
send_unreach(*pskb, ICMP_NET_ANO);
|
break;
|
break;
|
case IPT_ICMP_HOST_PROHIBITED:
|
case IPT_ICMP_HOST_PROHIBITED:
|
send_unreach(*pskb, ICMP_HOST_ANO);
|
send_unreach(*pskb, ICMP_HOST_ANO);
|
break;
|
break;
|
case IPT_ICMP_ADMIN_PROHIBITED:
|
case IPT_ICMP_ADMIN_PROHIBITED:
|
send_unreach(*pskb, ICMP_PKT_FILTERED);
|
send_unreach(*pskb, ICMP_PKT_FILTERED);
|
break;
|
break;
|
case IPT_TCP_RESET:
|
case IPT_TCP_RESET:
|
send_reset(*pskb, hooknum);
|
send_reset(*pskb, hooknum);
|
case IPT_ICMP_ECHOREPLY:
|
case IPT_ICMP_ECHOREPLY:
|
/* Doesn't happen. */
|
/* Doesn't happen. */
|
break;
|
break;
|
}
|
}
|
|
|
return NF_DROP;
|
return NF_DROP;
|
}
|
}
|
|
|
static int check(const char *tablename,
|
static int check(const char *tablename,
|
const struct ipt_entry *e,
|
const struct ipt_entry *e,
|
void *targinfo,
|
void *targinfo,
|
unsigned int targinfosize,
|
unsigned int targinfosize,
|
unsigned int hook_mask)
|
unsigned int hook_mask)
|
{
|
{
|
const struct ipt_reject_info *rejinfo = targinfo;
|
const struct ipt_reject_info *rejinfo = targinfo;
|
|
|
if (targinfosize != IPT_ALIGN(sizeof(struct ipt_reject_info))) {
|
if (targinfosize != IPT_ALIGN(sizeof(struct ipt_reject_info))) {
|
DEBUGP("REJECT: targinfosize %u != 0\n", targinfosize);
|
DEBUGP("REJECT: targinfosize %u != 0\n", targinfosize);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
/* Only allow these for packet filtering. */
|
/* Only allow these for packet filtering. */
|
if (strcmp(tablename, "filter") != 0) {
|
if (strcmp(tablename, "filter") != 0) {
|
DEBUGP("REJECT: bad table `%s'.\n", tablename);
|
DEBUGP("REJECT: bad table `%s'.\n", tablename);
|
return 0;
|
return 0;
|
}
|
}
|
if ((hook_mask & ~((1 << NF_IP_LOCAL_IN)
|
if ((hook_mask & ~((1 << NF_IP_LOCAL_IN)
|
| (1 << NF_IP_FORWARD)
|
| (1 << NF_IP_FORWARD)
|
| (1 << NF_IP_LOCAL_OUT))) != 0) {
|
| (1 << NF_IP_LOCAL_OUT))) != 0) {
|
DEBUGP("REJECT: bad hook mask %X\n", hook_mask);
|
DEBUGP("REJECT: bad hook mask %X\n", hook_mask);
|
return 0;
|
return 0;
|
}
|
}
|
|
|
if (rejinfo->with == IPT_ICMP_ECHOREPLY) {
|
if (rejinfo->with == IPT_ICMP_ECHOREPLY) {
|
printk("REJECT: ECHOREPLY no longer supported.\n");
|
printk("REJECT: ECHOREPLY no longer supported.\n");
|
return 0;
|
return 0;
|
} else if (rejinfo->with == IPT_TCP_RESET) {
|
} else if (rejinfo->with == IPT_TCP_RESET) {
|
/* Must specify that it's a TCP packet */
|
/* Must specify that it's a TCP packet */
|
if (e->ip.proto != IPPROTO_TCP
|
if (e->ip.proto != IPPROTO_TCP
|
|| (e->ip.invflags & IPT_INV_PROTO)) {
|
|| (e->ip.invflags & IPT_INV_PROTO)) {
|
DEBUGP("REJECT: TCP_RESET illegal for non-tcp\n");
|
DEBUGP("REJECT: TCP_RESET illegal for non-tcp\n");
|
return 0;
|
return 0;
|
}
|
}
|
}
|
}
|
|
|
return 1;
|
return 1;
|
}
|
}
|
|
|
static struct ipt_target ipt_reject_reg
|
static struct ipt_target ipt_reject_reg
|
= { { NULL, NULL }, "REJECT", reject, check, NULL, THIS_MODULE };
|
= { { NULL, NULL }, "REJECT", reject, check, NULL, THIS_MODULE };
|
|
|
static int __init init(void)
|
static int __init init(void)
|
{
|
{
|
if (ipt_register_target(&ipt_reject_reg))
|
if (ipt_register_target(&ipt_reject_reg))
|
return -EINVAL;
|
return -EINVAL;
|
return 0;
|
return 0;
|
}
|
}
|
|
|
static void __exit fini(void)
|
static void __exit fini(void)
|
{
|
{
|
ipt_unregister_target(&ipt_reject_reg);
|
ipt_unregister_target(&ipt_reject_reg);
|
}
|
}
|
|
|
module_init(init);
|
module_init(init);
|
module_exit(fini);
|
module_exit(fini);
|
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
|
|