How bonding driver takes RX packets from enslave interfaces - c

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.

Related

linux packet filtering by interface

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;
}

Add TCP Option to ACK packet during 3-way handshake

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.

Linux kernel UDP reception timestamp

I've been reading the network timestamping documentation of linux kernel and there is something that it's not clear to me.
Where is the timestamp provided by SO_TIMESTAMPNS generated? In hardware or in the kernel? If so it is gerated as soon as an interrupt for a new packet is raised?
SO_TIMESTAMPING should also allow the generation of hardware timestamps. Is this supported by all the NICs? How does SO_TIMESTAMPING with options SOF_TIMESTAMPING_RX_HARDWARE and SO_TIMESTAMPNS?
In that case is the hardware timestamp referring to the system clock or to the NIC clock? In the second case how to retrieve the NIC clock to compute elapsed time?
The socket attribute used for software timestamping is, SO_TIMESTAMPNS. This socket attribute returns the time from the system clock. It is not generated in the hardware, rather it is the snapshot of the system time when the interrupt is handled in the software. We can access this timestamp through the ancillary data (CMSG) that is not part of the socket payload, using:
int level, type;
struct cmsghdr *cm;
struct timespec *ts = NULL;
for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm))
{
level = cm->cmsg_level;
type = cm->cmsg_type;
if (SOL_SOCKET == level && SO_TIMESTAMPNS == type) {
ts = (struct timespec *) CMSG_DATA(cm);
printf("SW TIMESTAMP %ld.%09ld\n", (long)ts[0].tv_sec, (long)ts[0].tv_nsec);
}
}
The SO_TIMESTAMPING socket option offers many different flags, some of them are,
SOF_TIMESTAMPING_TX_HARDWARE // Transmit timestamp generated in hardware by NIC clock
SOF_TIMESTAMPING_RX_HARDWARE // Receive timestamp generated in hardware by NIC clock
SOF_TIMESTAMPING_TX_SOFTWARE // Transmit timestamp generated in kernel driver by NIC clock
SOF_TIMESTAMPING_RX_SOFTWARE // Receive timestamp generated in kernel driver by NIC clock
This socket option is not supported by all Network Interface Cards (NICs). Currently many ethernet NICs support SO_TIMESTAMPING. In order to find if a particular interface driver supports SO_TIMESTAMPING, use:
ethtool -T ethX // where X corresponds to your particular interface
This will return all the socket attributes ethX supports for timestamping.
To use hardware timestamping feature provided by a particular NIC, use the code:
int flags;
flags = SOF_TIMESTAMPING_TX_HARDWARE
| SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_TX_SOFTWARE
| SOF_TIMESTAMPING_RX_SOFTWARE
| SOF_TIMESTAMPING_RAW_HARDWARE;
if (setsockopt(sd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags)) < 0)
printf("ERROR: setsockopt SO_TIMESTAMPING\n");
int level, type;
struct cmsghdr *cm;
struct timespec *ts = NULL;
for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm))
{
if (SOL_SOCKET == level && SO_TIMESTAMPING == type) {
ts = (struct timespec *) CMSG_DATA(cm);
printf("HW TIMESTAMP %ld.%09ld\n", (long)ts[2].tv_sec, (long)ts[2].tv_nsec);
}
}

Unicast UDP packet miss

Unicast(one to one) UDP communication, each time the packet received is not same; if I am sending 1000 packets within interval of 500ms I get 9 packets missed. I am working on windows platform VCC 6.0; using sendto system call to send the Ethernet packet. In the host side I miss the packets by checksum error or header error.
Please let me know if you need more details. My agenda is that I should not miss any packets in target side.
Any help regarding this issue will be highly appreciated.
{
//Initialize local variables
MAINAPP(pAppPtr);
int iResult = 0;
int sRetVal = 0;
static char cTransmitBuffer[1024];
unsigned long ulTxPacketLength =0;
int in_usTimeOut = 0;
unsigned short usTimeout = 0;
S_QJB_POWER_CNTRL S_Out_QJB_Power_Cntrl = {0};
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_ucHeader[0] = QJB_TCP_HEADER_BYTE1;
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_ucHeader[1] = QJB_TCP_HEADER_BYTE2;
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_usCmdID = QJB_ETH_POWER_ON;
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_usCmdResults = 0;
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_usDataSize = sizeof(S_QJB_POWER_CNTRL);
//Fill the controls & delay
sRetVal = PowerCntrlStructFill(&pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.U_Tcp_Msg.S_QJB_PowerCntrl,&usTimeout);
if(sRetVal)
{
return sRetVal;
}
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_usReserved = 0;
pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.m_usChecksum = 0;
//Perform Endian Swap
pAppPtr->objEndianConv.EndianSwap(&pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg.U_Tcp_Msg.S_QJB_PowerCntrl, &S_Out_QJB_Power_Cntrl);
//Frame the transmission packet
QJB_Frame_TXBuffer(cTransmitBuffer, &(pAppPtr->S_Tcp_Handle.Tcp_Tx_Msg), &ulTxPacketLength,(void *)&S_Out_QJB_Power_Cntrl);
//Send the data to the target
iResult = sendto(pAppPtr->sktConnectSocket,cTransmitBuffer,ulTxPacketLength,0,(struct sockaddr *)&pAppPtr->g_dest_sin, sizeof(pAppPtr->g_dest_sin));
if(iResult == SOCKET_ERROR)
{
return QJB_TARGET_DISCONNECTED;
}
memset(&pAppPtr->S_Tcp_Handle.Tcp_Rx_Msg,0,sizeof(S_QJB_ETHERNET_PKT));// 1336
//Send the Command and obtain the response
sRetVal = QJB_ETHResRev(pAppPtr->sktConnectSocket,&pAppPtr->S_Tcp_Handle.Tcp_Rx_Msg,3);
return sRetVal;
}
Sathishkumar.
Unfortunately, since UDP makes no guarantees about delivery, the network stack can drop your sent packets at any time for any reason. It is worth noting there is no order gurantee of the orders the packets will arrive in as well.
If ordering and delivery is prime to your application, which I think it is, consider switching to TCP.

Linux kernel: packet is forwarded to another interface, but isn't sent onwards

I've made a virtual network interface that forwards any packet it receives to a physical interface eth2, with the intention of sending it to whichever destination is required.
The virtual interface's hard_start_xmit function receives the packet, changes the sourceMAC and IP, sets the handling device to be the physical interface, and sets the packet type to PACKET_OUTGOING.
static int forward_packet(struct sk_buff *skb, struct net_device *dev) {
char switched_device_name[] = "eth2";
struct net_device *switched_device = dev_get_by_name(switched_device_name);
int ifrx_output;
u32 switched_device_ip;
if (!switched_device) {
printk("Error: Couldn't get device %s for switching.\n", switched_device_name);
return -1;
}
skb->dev = switched_device;
skb->input_dev = switched_device;
skb->pkt_type = PACKET_OUTGOING;
switched_device_ip = get_interface_ip(switched_device);
change_source_mac_and_ip(skb, switched_device->dev_addr, switched_device_ip);
ifrx_output = netif_rx(skb);
printk("netif_rx returned %d.\n", ifrx_output);
return 0;
}
I tested this code, and while the packet does appear in the physical interface's tcpdump, there's no trace of it in the destination machine.
The only reason I can think something like this would happen is because the physical interface treats it as an incoming packet with no need to pass it on. However as far as I can tell, only skb->pkt_type determines that.
What other packet changes could I be missing?

Resources