Blame src/extra/tcp.c

Packit c43939
/*
Packit c43939
 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
Packit c43939
 *
Packit c43939
 * This program is free software; you can redistribute it and/or modify
Packit c43939
 * it under the terms of the GNU General Public License as published by
Packit c43939
 * the Free Software Foundation; either version 2 of the License, or
Packit c43939
 * (at your option) any later version.
Packit c43939
 *
Packit c43939
 * This code has been sponsored by Vyatta Inc. <http://www.vyatta.com>
Packit c43939
 */
Packit c43939
Packit c43939
#include <stdio.h>
Packit c43939
#include <string.h> /* for memcpy */
Packit c43939
#include <stdbool.h>
Packit c43939
#include <arpa/inet.h>
Packit c43939
#include <netinet/ip.h>
Packit c43939
#include <netinet/ip6.h>
Packit c43939
#define _GNU_SOURCE
Packit c43939
#include <netinet/tcp.h>
Packit c43939
Packit c43939
#include <libnetfilter_queue/libnetfilter_queue.h>
Packit c43939
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
Packit c43939
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
Packit c43939
#include <libnetfilter_queue/libnetfilter_queue_ipv6.h>
Packit c43939
#include <libnetfilter_queue/pktbuff.h>
Packit c43939
Packit c43939
#include "internal.h"
Packit c43939
Packit c43939
/**
Packit c43939
 * \defgroup tcp TCP helper functions
Packit c43939
 * @{
Packit c43939
 */
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_get_hdr - get the TCP header
Packit c43939
 * \param pktb: pointer to user-space network packet buffer
Packit c43939
 * \returns validated pointer to the TCP header or NULL if the TCP header was
Packit c43939
 * not set or if a minimal length check fails.
Packit c43939
 * \note You have to call nfq_ip_set_transport_header() or
Packit c43939
 * nfq_ip6_set_transport_header() first to set the TCP header.
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
struct tcphdr *nfq_tcp_get_hdr(struct pkt_buff *pktb)
Packit c43939
{
Packit c43939
	if (pktb->transport_header == NULL)
Packit c43939
		return NULL;
Packit c43939
Packit c43939
	/* No room for the TCP header. */
Packit c43939
	if (pktb_tail(pktb) - pktb->transport_header < sizeof(struct tcphdr))
Packit c43939
		return NULL;
Packit c43939
Packit c43939
	return (struct tcphdr *)pktb->transport_header;
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_get_payload - get the TCP packet payload
Packit c43939
 * \param tcph: pointer to the TCP header
Packit c43939
 * \param pktb: pointer to user-space network packet buffer
Packit c43939
 * \returns Pointer to the TCP payload, or NULL if malformed TCP packet.
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
void *nfq_tcp_get_payload(struct tcphdr *tcph, struct pkt_buff *pktb)
Packit c43939
{
Packit c43939
	unsigned int len = tcph->doff * 4;
Packit c43939
Packit c43939
	/* TCP packet is too short */
Packit c43939
	if (len < sizeof(struct tcphdr))
Packit c43939
		return NULL;
Packit c43939
Packit c43939
	/* malformed TCP data offset. */
Packit c43939
	if (pktb->transport_header + len > pktb_tail(pktb))
Packit c43939
		return NULL;
Packit c43939
Packit c43939
	return pktb->transport_header + len;
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_get_payload_len - get the tcp packet payload
Packit c43939
 * \param tcph: pointer to the TCP header
Packit c43939
 * \param pktb: pointer to user-space network packet buffer
Packit c43939
 * \returns Length of TCP payload (user data)
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
unsigned int nfq_tcp_get_payload_len(struct tcphdr *tcph, struct pkt_buff *pktb)
Packit c43939
{
Packit c43939
	return pktb_tail(pktb) - pktb->transport_header - (tcph->doff * 4);
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * \defgroup tcp_internals Internal TCP functions
Packit c43939
 *
Packit c43939
 * Most user-space programs will never need these.
Packit c43939
 *
Packit c43939
 * @{
Packit c43939
 */
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_compute_checksum_ipv4 - computes IPv4/TCP packet checksum
Packit c43939
 * \param tcph: pointer to the TCP header
Packit c43939
 * \param iph: pointer to the IPv4 header
Packit c43939
 * \note
Packit c43939
 * nfq_tcp_mangle_ipv4() invokes this function.
Packit c43939
 * As long as developers always use __nfq_tcp_mangle_ipv4__ when changing the
Packit c43939
 * content of a TCP message, there is no need to call
Packit c43939
 * __nfq_tcp_compute_checksum_ipv4__.
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
void nfq_tcp_compute_checksum_ipv4(struct tcphdr *tcph, struct iphdr *iph)
Packit c43939
{
Packit c43939
	/* checksum field in header needs to be zero for calculation. */
Packit c43939
	tcph->check = 0;
Packit c43939
	tcph->check = nfq_checksum_tcpudp_ipv4(iph, IPPROTO_TCP);
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_compute_checksum_ipv6 - computes IPv6/TCP packet checksum
Packit c43939
 * \param tcph: pointer to the TCP header
Packit c43939
 * \param ip6h: pointer to the IPv6 header
Packit c43939
 * \note
Packit c43939
 * nfq_tcp_mangle_ipv6() invokes this function.
Packit c43939
 * As long as developers always use __nfq_tcp_mangle_ipv6__ when changing the
Packit c43939
 * content of a TCP message, there is no need to call
Packit c43939
 * __nfq_tcp_compute_checksum_ipv6__.
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
void nfq_tcp_compute_checksum_ipv6(struct tcphdr *tcph, struct ip6_hdr *ip6h)
Packit c43939
{
Packit c43939
	/* checksum field in header needs to be zero for calculation. */
Packit c43939
	tcph->check = 0;
Packit c43939
	tcph->check = nfq_checksum_tcpudp_ipv6(ip6h, tcph, IPPROTO_TCP);
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * @}
Packit c43939
 */
Packit c43939
Packit c43939
/*
Packit c43939
 *	The union cast uses a gcc extension to avoid aliasing problems
Packit c43939
 *  (union is compatible to any of its members)
Packit c43939
 *  This means this part of the code is -fstrict-aliasing safe now.
Packit c43939
 */
Packit c43939
union tcp_word_hdr {
Packit c43939
	struct tcphdr hdr;
Packit c43939
	uint32_t  words[5];
Packit c43939
};
Packit c43939
Packit c43939
#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words[3])
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_pkt_snprintf_tcp_hdr - print tcp header into one buffer in a humnan
Packit c43939
 * readable way
Packit c43939
 * \param buf: pointer to buffer that is used to print the object
Packit c43939
 * \param size: size of the buffer (or remaining room in it).
Packit c43939
 * \param tcph: pointer to a valid tcp header.
Packit c43939
 * \returns Same as \b snprintf
Packit c43939
 * \sa __snprintf__(3)
Packit c43939
 *
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
int nfq_tcp_snprintf(char *buf, size_t size, const struct tcphdr *tcph)
Packit c43939
{
Packit c43939
	int ret, len = 0;
Packit c43939
Packit c43939
#define TCP_RESERVED_BITS htonl(0x0F000000)
Packit c43939
Packit c43939
	ret = snprintf(buf, size, "SPT=%u DPT=%u SEQ=%u ACK=%u "
Packit c43939
				   "WINDOW=%u RES=0x%02x ",
Packit c43939
			ntohs(tcph->source), ntohs(tcph->dest),
Packit c43939
			ntohl(tcph->seq), ntohl(tcph->ack_seq),
Packit c43939
			ntohs(tcph->window),
Packit c43939
			(uint8_t)
Packit c43939
			(ntohl(tcp_flag_word(tcph) & TCP_RESERVED_BITS) >> 22));
Packit c43939
	len += ret;
Packit c43939
Packit c43939
	if (tcph->urg) {
Packit c43939
		ret = snprintf(buf+len, size-len, "URG ");
Packit c43939
		len += ret;
Packit c43939
	}
Packit c43939
	if (tcph->ack) {
Packit c43939
		ret = snprintf(buf+len, size-len, "ACK ");
Packit c43939
		len += ret;
Packit c43939
	}
Packit c43939
	if (tcph->psh) {
Packit c43939
		ret = snprintf(buf+len, size-len, "PSH ");
Packit c43939
		len += ret;
Packit c43939
	}
Packit c43939
	if (tcph->rst) {
Packit c43939
		ret = snprintf(buf+len, size-len, "RST ");
Packit c43939
		len += ret;
Packit c43939
	}
Packit c43939
	if (tcph->syn) {
Packit c43939
		ret = snprintf(buf+len, size-len, "SYN ");
Packit c43939
		len += ret;
Packit c43939
	}
Packit c43939
	if (tcph->fin) {
Packit c43939
		ret = snprintf(buf+len, size-len, "FIN ");
Packit c43939
		len += ret;
Packit c43939
	}
Packit c43939
	/* XXX: Not TCP options implemented yet, sorry. */
Packit c43939
Packit c43939
	return ret;
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_mangle_ipv4 - mangle TCP/IPv4 packet buffer
Packit c43939
 * \param pktb: pointer to network packet buffer
Packit c43939
 * \param match_offset: offset to content that you want to mangle
Packit c43939
 * \param match_len: length of the existing content you want to mangle
Packit c43939
 * \param rep_buffer: pointer to data you want to use to replace current content
Packit c43939
 * \param rep_len: length of data you want to use to replace current content
Packit c43939
 * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
Packit c43939
 * \note This function updates the IPv4 length and recalculates the IPv4 & TCP
Packit c43939
 * checksums for you.
Packit c43939
 * \warning After changing the length of a TCP message, the application will
Packit c43939
 * need to mangle sequence numbers in both directions until another change
Packit c43939
 * puts them in sync again
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
int nfq_tcp_mangle_ipv4(struct pkt_buff *pktb,
Packit c43939
			unsigned int match_offset, unsigned int match_len,
Packit c43939
			const char *rep_buffer, unsigned int rep_len)
Packit c43939
{
Packit c43939
	struct iphdr *iph;
Packit c43939
	struct tcphdr *tcph;
Packit c43939
Packit c43939
	iph = (struct iphdr *)pktb->network_header;
Packit c43939
	tcph = (struct tcphdr *)(pktb->network_header + iph->ihl*4);
Packit c43939
Packit c43939
	if (!nfq_ip_mangle(pktb, iph->ihl*4 + tcph->doff*4,
Packit c43939
				match_offset, match_len, rep_buffer, rep_len))
Packit c43939
		return 0;
Packit c43939
Packit c43939
	nfq_tcp_compute_checksum_ipv4(tcph, iph);
Packit c43939
Packit c43939
	return 1;
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * nfq_tcp_mangle_ipv6 - Mangle TCP/IPv6 packet buffer
Packit c43939
 * \param pktb: Pointer to network packet buffer
Packit c43939
 * \param match_offset: Offset from start of TCP data of content that you want
Packit c43939
 * to mangle
Packit c43939
 * \param match_len: Length of the existing content you want to mangle
Packit c43939
 * \param rep_buffer: Pointer to data you want to use to replace current content
Packit c43939
 * \param rep_len: Length of data you want to use to replace current content
Packit c43939
 * \returns 1 for success and 0 for failure. See pktb_mangle() for failure case
Packit c43939
 * \note This function updates the IPv6 length and recalculates the TCP
Packit c43939
 * checksum for you.
Packit c43939
 * \warning After changing the length of a TCP message, the application will
Packit c43939
 * need to mangle sequence numbers in both directions until another change
Packit c43939
 * puts them in sync again
Packit c43939
 */
Packit c43939
EXPORT_SYMBOL
Packit c43939
int nfq_tcp_mangle_ipv6(struct pkt_buff *pktb,
Packit c43939
			unsigned int match_offset, unsigned int match_len,
Packit c43939
			const char *rep_buffer, unsigned int rep_len)
Packit c43939
{
Packit c43939
	struct ip6_hdr *ip6h;
Packit c43939
	struct tcphdr *tcph;
Packit c43939
Packit c43939
	ip6h = (struct ip6_hdr *)pktb->network_header;
Packit c43939
	tcph = (struct tcphdr *)(pktb->transport_header);
Packit c43939
	if (!tcph)
Packit c43939
		return 0;
Packit c43939
Packit c43939
	if (!nfq_ip6_mangle(pktb,
Packit c43939
			   pktb->transport_header - pktb->network_header +
Packit c43939
			   tcph->doff * 4,
Packit c43939
			   match_offset, match_len, rep_buffer, rep_len))
Packit c43939
		return 0;
Packit c43939
Packit c43939
	nfq_tcp_compute_checksum_ipv6(tcph, ip6h);
Packit c43939
Packit c43939
	return 1;
Packit c43939
}
Packit c43939
Packit c43939
/**
Packit c43939
 * @}
Packit c43939
 */