I'm writing kernel module using netfilter. I just want to handle ACK for SYN/ACK (TCP three-way handshake). I use skb_is_tcp_pure_ack function, but ACK for data is also processed.
How can I do? My kernel version is 3.10.0-514.16.1.el7.x86_64.
Current code looks like this:
struct iphdr *iph;
struct tcphdr *tcph;
struct net *net;
unsigned int hdr_len;
unsigned int tcphoff;
if (!skb_is_tcp_pure_ack(skb)) {
return NF_ACCEPT;
}
/* add tcp option */
/* A netfilter instance to use */
static struct nf_hook_ops nfho __read_mostly = {
.hook = ato_hookfn,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_LAST,
.owner = THIS_MODULE,
};
From the TCP state machine, you want to only match ACK packets when the state is TCP_SYN_SENT. Try adding another condition to check that, something like:
if (skb_is_tcp_pure_ack(skb)
&& skb->sk->sk_state == TCP_SYN_SENT) {
/*
* ACK(-only) packet during three-way handshake
*/
}
Also, note that skb_is_tcp_pure_ack was introduced in kernel version 4.* and not below.
Related
I'm trying to write my own network packet filter module to replace iptables on my custom linux system.
I want the filter to drop all packets arriving from port 80 except the ones coming from the interface wlan0.
I hoped there would be a interface member in the struct iphdr but it seems to not be there (which makes sense since I think the interface is part of the 2nd layer). does anyone know how I can acheive this?
this is my code and I'm wandering what to put in the if statement that will complete the check:
static unsigned int pkg_filter_handler(void *priv, struct sk_buff *skb, const struct nf_hook_state *state)
{
if (!skb) {
return NF_ACCEPT;
}
/* check if loopback */
u32 srcAddr;
struct iphdr *ipHeader = ip_hdr(skb);
/* check if TCP protocol */
else if (iphdr->protocol == IPPROTO_TCP) {
/* check interface */
if (/*TODO: accept wlan0 rcp packets ad drop the rest*/) {
return NF_ACCEPT;
}
return NF_DROP;
}
return NF_ACCEPT;
}
I am using l2fwd-dpdk application from which I can extract 5-tuples, and can see if DNS Packet is present or not.
Now I want to classify the DNS Packet using dpdk, for which I am failing.
Here is my code.
struct rte_udp_hdr *udp_hdr;
struct dnshdr *dns_hdr;
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53)
{
printf("DNS Packet");
char *dns_hdr = (char *)udp_hdr + sizeof(rte_udp_hdr);
}
I want to separate
Flags
Rdata
Class
TTL
and save them separately. Is there any way around, I can comfortable to use cpp wrapper as well.
DPDK as of 21.08 does not house any header or structure to typecast to DNS packet. Hence easiest way to solve the issue as mentioned by #wildplasser is to declare your custom DNS header and use it. In your code snippet, you already have struct dnshdr *dns_hdr; So the easier way is to modify your existing code to reflect
struct rte_udp_hdr *udp_hdr;
struct dnshdr *dns_hdr;
/* use DPDK mtod API to get the start of ethernet frame */
/* check for packet size, ether type, IP protocol */
/* update udp_hdr to position in the packet */
if (rte_be_to_cpu_16(udp_hdr->dst_port) == 53)
{
printf("DNS Packet");
struct dnshdr *dns_hdr = (struct dnshdr *)((char *)udp_hdr + sizeof(rte_udp_hdr));
}
Note: Possible structure definition code snippet would be
typedef struct {
uint16_t id;
uint16_t rd:1;
uint16_t tc:1;
uint16_t aa:1;
uint16_t opcode:4;
uint16_t qr:1;
uint16_t rcode:4;
uint16_t zero:3;
uint16_t ra:1;
uint16_t qcount; /* question count */
uint16_t ancount; /* Answer record count */
uint16_t nscount; /* Name Server (Autority Record) Count */
uint16_t adcount; /* Additional Record Count */
} custom_dnshdr;
custom_dnshdr *dns_hdr = (custom_dnshdr *) ((char *)udp_hdr + sizeof(rte_udp_hdr));
There are functions in Linux for getting Ethernet header, IP header,and UDP headers likes these
udp_hdr(skb)
ip_hdr
skb_push(skb, ETH_HLEN)
But I could not find any function for getting payload like body of a packet like i.e. which contains body so I can write HTTP or other protocol data. in Linux Device Driver book or after searching couldn't find it. so question is how to compose UDP packet with Ethernet, IP, UDP headers and payload in kernel?
any function for getting payload like body of a packet
You can access payload different ways depending on what you want to do the next time. E.g.:
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_UDP) {
struct udphdr *udph = udp_hdr(skb);
// E.g. check for UDP port
struct myl7_header *l7h = (struct myl7_header *)(udph + sizeof(struct udphdr));
// ...
}
Or you can pull the network and transport headers if you want to reconstruct encapsulation further or they are no longer needed (rough example, not with all possible sanity checks):
struct iphdr *iph = ip_hdr(skb);
if (iph->protocol == IPPROTO_UDP) {
struct udphdr *udph = udp_hdr(skb);
struct myl7_header *l7h;
// E.g. check for UDP port
skb_pull(skb, sizeof(struct iphdr));
skb_pull(skb, sizeof(struct udphdr));
l7h = (struct myl7_header *)skb->data;
// tansport protocol payload's length:
// skb->len or skb_tail_pointer(skb) - skb->data
}
I don't really know what you mean by payloadhtml, L7-protocol it not so kernel specific thing, so in general we are talking about transport protocol's payload.
N.B.: ip_hdr(), udp_hdr() functions imply that non-paged (linear) skb is used.
Related: What's the correct way to process all the payload of a sk_buff packet in Linux
I'm trying to send some manually crafted ARP packets over the network,more specifically an ARP request to get the MAC address of a host.
I can't get the final packet right, on wireshark it stills shows some inconsistency.
Let me walk you through :
Here are the struct & typedef I use all over the program ,
I've defined
a IP struct ( => in_addr )
a MAC struct ( => ether_addr )
a Host struct composed of a MAC & IP
Custom struct to represent a Ethernet frame & an ARP frame.
The code:
#define ETH_ADDR_SIZE 6
#define IP_ADDR_SIZE 4
typedef u_char Packet;
typedef struct in_addr IP;
typedef struct ether_addr MAC;
struct Host {
IP ip;
MAC mac;
};
typedef struct pkt_eth {
MAC dest;
MAC src;
u_short type;
} pkt_eth;
typedef struct pkt_arp {
u_short htype;/* hardware type => ethernet , etc */
u_short ptype; /*protocol type => ipv4 or ipv6 */
u_char hard_addr_len; /* usually 6 bytes for ethernet */
u_char proto_addr_len; /*usually 8 bytes for ipv4 */
u_short opcode; /* type of arp */
MAC hard_addr_send;
IP proto_addr_send;
MAC hard_addr_dest;
IP proto_addr_dest;
} pkt_arp;
/* Designate our own MAC / IP addresses of the interface */
extern MAC mac;
extern IP ip;
extern char * interface;
/* Just some vars used to compare with the struct we use */
const MAC broadcast_mac = { 0xff,0xff,0xff,0xff,0xff,0xff };
const MAC null_mac = { 0x00,0x00,0x00,0x00,0x00,0x00 };
const IP broadcast_ip = { 0xffffffff };
const IP null_ip = { 0x00000000 };
const struct Host null_host = {{ 0x00000000 },
{ 0x00,0x00,0x00,0x00,0x00,0x00 }};
/* Empty mac address which can be used as a temp variable */
MAC tmp_mac = { 0x00,0x00,0x00,0x00,0x00,0x00 };
IP tmp_ip = { 0x00000000 };
Here is the relevant function :
int
arp_resolve_mac ( struct Host * host )
{
struct pkt_arp * arp;
struct pkt_eth * eth;
/*Create the request packet */
Packet * request = arp_packet(REQUEST);
eth = (struct pkt_eth *) (request);
arp = (struct pkt_arp *) (request + ETH_SIZE);
/* ethernet frame */
copy_mac(ð->dest,&broadcast_mac);
copy_mac(ð->src,&mac);
/* arp request => mac dest address set to null */
copy_mac(&arp->hard_addr_send,&mac);
copy_mac(&arp->hard_addr_dest,&null_mac);
/* arp request => target ip ! */
copy_ip(&arp->proto_addr_send,&ip);
copy_ip(&arp->proto_addr_dest,&host->ip);
/* Set up sniffing. Better to do it before so less
* prepare time and if any error occurs, no need to send
* the packet. less intrusive */
pcap_init(interface,"arp");
pcap_set_arp_analyzer(arp_analyzer_resolv);
/* Sets the tmp ip variable so we will know if it the right
* response we get or a response coming from another source */
tmp_ip = host->ip;
/* sends the packet */
if(pcap_send_packet(request,ARP_PACKET_SIZE) == -1) {
fprintf(stderr,"Error while sending ARP request packet.\n");
return -1;
}
....
}
Packet *
arp_packet ( int opcode )
{
struct pkt_arp * arp;
struct pkt_eth * eth;
Packet * bytes = (Packet *) malloc(ARP_PACKET_SIZE);
if(bytes == NULL) {
fprintf(stderr,"Could not alloc ARP packet.\n");
return NULL;
}
eth = (struct pkt_eth *) (bytes);
eth->type = htons(ETHERTYPE_ARP);
/* length about hard / proto ... */
arp = (struct pkt_arp *) (bytes + ETH_SIZE);
arp->htype = htons(1);
arp->ptype = htons(0x0800);
arp->hard_addr_len = ETH_ADDR_SIZE;
arp->proto_addr_len = IP_ADDR_SIZE;
/* reply or request */
arp->opcode = opcode == REQUEST ? htons(ARPOP_REQUEST) : htons(ARPOP_REPLY);
return bytes;
} /* ----- end of function arp_empty ----- */
void copy_mac(MAC * m1,const MAC * m2) {
memcpy(m1,m2,ETH_ADDR_SIZE);
}
void copy_ip(IP * i1,const IP * i2) {
memcpy(i1,i2,IP_ADDR_SIZE);
}
void copy_host(struct Host * h1,const struct Host * h2) {
copy_mac(&h1->mac,&h2->mac);
copy_ip(&h1->ip,&h2->ip);
}
Problem:
The created packet is not quite right. Everything is fine up to the hard_addr_send. After this field, there is 2 bytes 0x00,0x00, (seen in GDB) and then the IP address. But due to this offset, it's impossible to correctly parse this packet. For example,in wireshark, instead of getting "10.0.0.1", I've got "0.0.10.0" for IP.
Here is the transcript of GDB :
/** 14 to pass ethernet frame & 4 + 2 + 2 to go to the addresses section*/
(gdb) x/6xb request+14+4+2+2
/** My MAC address , field hard_addr_send. it's GOOD. */
0x606b16: 0x34 0x67 0x20 0x01 0x9a 0x67
(gdb) x/6xb request+14+4+2+2+6
/** 6bytes later, supposedly my IP address.
* It should be 10.0.0.7 but you can see the 0x0a shifted by 2 bytes */
0x606b1c: 0x00 0x00 0x0a 0x00 0x00 0x07
In the method "arp_resolv_mac", everything info is right, i.e. struct Host contains the good information etc; I've checked everything.
I just don't get this offset by 2 bytes ... In a older versions, not using all theses new structs (only char *), I've already succeed at creating a right ARP packet, so I'm kind of wondering if this is not due to the struct, but my knowledge of C does not extend to the memory alignement subject ...!
Thank you.
The problem is that your structs are not packed. One solution would be to use packed structs, i.e.
typedef struct __attribute__ ((__packed__)) pkt_arp {
u_short htype;/* hardware type => ethernet , etc */
u_short ptype; /*protocol type => ipv4 or ipv6 */
u_char hard_addr_len; /* usually 6 bytes for ethernet */
u_char proto_addr_len; /*usually 8 bytes for ipv4 */
u_short opcode; /* type of arp */
MAC hard_addr_send;
IP proto_addr_send;
MAC hard_addr_dest;
IP proto_addr_dest;
} pkt_arp;
However, that is a gcc-specific extension other compilers may not support.
In my opinion, the best solution is accessing the elements of the byte array directly instead of using structs. Yes, it adds a few lines of code, but it's guaranteed to work for compilers that don't implement packed structs too.
I have a question regarding how to bonding driver takes RX packets from enslaved interfaces. I found that bonding use dev_add_pack() to set handlers for LACPDU and ARP packets, but I didn't found other handlers (for other packets types).
Could you please help me to solve this problem?
Bonding drivers registers its own Rx handler, when a slave interface is enslaved to a bond master, in bond_enslave() you can see:
res = netdev_rx_handler_register(slave_dev, bond_handle_frame,
new_slave);
So in bond_handle_frame(), it will hijack the packets received by the slave interface, so that the bond master will receive the packets instead:
static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
{
struct sk_buff *skb = *pskb;
struct slave *slave;
struct bonding *bond;
int (*recv_probe)(const struct sk_buff *, struct bonding *,
struct slave *);
int ret = RX_HANDLER_ANOTHER;
skb = skb_share_check(skb, GFP_ATOMIC);
if (unlikely(!skb))
return RX_HANDLER_CONSUMED;
*pskb = skb;
slave = bond_slave_get_rcu(skb->dev);
bond = slave->bond;
if (bond->params.arp_interval)
slave->dev->last_rx = jiffies;
recv_probe = ACCESS_ONCE(bond->recv_probe);
if (recv_probe) {
ret = recv_probe(skb, bond, slave);
if (ret == RX_HANDLER_CONSUMED) {
consume_skb(skb);
return ret;
}
}
if (bond_should_deliver_exact_match(skb, slave, bond)) {
return RX_HANDLER_EXACT;
}
skb->dev = bond->dev;
if (bond->params.mode == BOND_MODE_ALB &&
bond->dev->priv_flags & IFF_BRIDGE_PORT &&
skb->pkt_type == PACKET_HOST) {
if (unlikely(skb_cow_head(skb,
skb->data - skb_mac_header(skb)))) {
kfree_skb(skb);
return RX_HANDLER_CONSUMED;
}
memcpy(eth_hdr(skb)->h_dest, bond->dev->dev_addr, ETH_ALEN);
}
return ret;
}
I checked code of bonding and found that driver didn't inspect incoming RX packets without some of types (LACPDU, ARP) in cases, when driver works in these modes. Driver set handlers for this packets with use of dev_add_pack() function.
For set global hook in practic you can use nf_register_hook(), which provide interface for setup own net filter procedure for intercept packets.
Seems that nf_register_hook() is more powerful than dev_add_pack(), but I think that need more care when working nf_register_hook(), because it can drop a lot of packets in case of wrong conditions in hook.