tcp.c
/*
* (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
*/
#include <stdio.h>
#include <string.h> /* for memcpy */
#include <stdbool.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#define _GNU_SOURCE
#include <netinet/tcp.h>
#include <libnetfilter_queue/libnetfilter_queue.h>
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
#include <libnetfilter_queue/pktbuff.h>
#include "internal.h"
EXPORT_SYMBOL
struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb)
{
if (pktb->transport_header == NULL)
return NULL;
/* No room for the TCP header. */
if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct tcphdr))
return NULL;
return (struct tcphdr *)pktb->transport_header;
}
EXPORT_SYMBOL
void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb)
{
unsigned int len = tcph->doff * 4;
/* TCP packet is too short */
if (len < sizeof(struct tcphdr))
return NULL;
/* malformed TCP data offset. */
if (pktb->transport_header + len > pktb_tail(pktb))
return NULL;
return pktb->transport_header + len;
}
EXPORT_SYMBOL
unsigned int nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb)
{
return pktb_tail(pktb) - pktb->transport_header - (tcph->doff * 4);
}
EXPORT_SYMBOL
void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph)
{
/* checksum field in header needs to be zero for calculation. */
tcph->check = 0;
tcph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_TCP);
}
EXPORT_SYMBOL
void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h)
{
/* checksum field in header needs to be zero for calculation. */
tcph->check = 0;
tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP);
}
/*
* The union cast uses a gcc extension to avoid aliasing problems
* (union is compatible to any of its members)
* This means this part of the code is -fstrict-aliasing safe now.
*/
union tcp_word_hdr {
struct tcphdr hdr;
uint32_t words[5];
};
#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words[3])
EXPORT_SYMBOL
int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph)
{
int ret, len = 0;
#define TCP_RESERVED_BITS htonl(0x0F000000)
ret = snprintf(buf, size, "SPT=%u DPT=%u SEQ=%u ACK=%u "
"WINDOW=%u RES=0x%02x ",
ntohs(tcph->source), ntohs(tcph->dest),
ntohl(tcph->seq), ntohl(tcph->ack_seq),
ntohs(tcph->window),
(uint8_t)
(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
len += ret;
if (tcph->urg) {
ret = snprintf(buf+len, size-len, "URG ");
len += ret;
}
if (tcph->ack) {
ret = snprintf(buf+len, size-len, "ACK ");
len += ret;
}
if (tcph->psh) {
ret = snprintf(buf+len, size-len, "PSH ");
len += ret;
}
if (tcph->rst) {
ret = snprintf(buf+len, size-len, "RST ");
len += ret;
}
if (tcph->syn) {
ret = snprintf(buf+len, size-len, "SYN ");
len += ret;
}
if (tcph->fin) {
ret = snprintf(buf+len, size-len, "FIN ");
len += ret;
}
/* XXX: Not TCP options implemented yet, sorry. */
return ret;
}
EXPORT_SYMBOL
int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb,
unsigned int match_offset, unsigned int match_len,
const char *rep_buffer, unsigned int rep_len)
{
struct iphdr *iph;
struct tcphdr *tcph;
iph = (struct iphdr *)pktb->network_header;
tcph = (struct tcphdr *)(pktb->network_header + iph->ihl*4);
if (!nfq_ip_mangle(pktb, iph->ihl*4 + tcph->doff*4,
match_offset, match_len, rep_buffer, rep_len))
return 0;
nfq_tcp_compute_checksum_ipv4(tcph, iph);
return 1;
}
EXPORT_SYMBOL
int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb,
unsigned int match_offset, unsigned int match_len,
const char *rep_buffer, unsigned int rep_len)
{
struct ip6_hdr *ip6h;
struct tcphdr *tcph;
ip6h = (struct ip6_hdr *)pktb->network_header;
tcph = (struct tcphdr *)(pktb->transport_header);
if (!tcph)
return 0;
if (!nfq_ip6_mangle(pktb,
pktb->transport_header - pktb->network_header +
tcph->doff * 4,
match_offset, match_len, rep_buffer, rep_len))
return 0;
nfq_tcp_compute_checksum_ipv6(tcph, ip6h);
return 1;
}
nfq_ip_mangle
int nfq_ip_mangle(struct pkt_buff *pktb, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len)
Definition ipv4.c:127
nfq_ip6_mangle
int nfq_ip6_mangle(struct pkt_buff *pktb, unsigned int dataoff, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len)
Definition ipv6.c:131
nfq_tcp_compute_checksum_ipv6
void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h)
Definition tcp.c:126
nfq_tcp_compute_checksum_ipv4
void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph)
Definition tcp.c:108
nfq_tcp_get_payload
void * nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb)
Definition tcp.c:62
nfq_tcp_snprintf
int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph)
Definition tcp.c:160
nfq_tcp_mangle_ipv4
int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len)
Definition tcp.c:219
nfq_tcp_mangle_ipv6
int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb, unsigned int match_offset, unsigned int match_len, const char *rep_buffer, unsigned int rep_len)
Definition tcp.c:254
nfq_tcp_get_payload_len
unsigned int nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb)
Definition tcp.c:84
nfq_tcp_get_hdr
struct tcphdr * nfq_tcp_get_hdr(struct pkt_buff *pktb)
Definition tcp.c:43