Using Linux, How to Specify Which Ethernet Interface Data Is Transmitted On

Using Linux, how to specify which ethernet interface data is transmitted on

No offense intended, but the answer about using bind() is quite wrong. bind() will control the source IP address placed within the packet IP header. It does not control which interface will be used to send the packet: the kernel's routing table will be consulted to determine which interface has the lowest cost to reach a particular destination. (*see note)

Instead, you should use an SO_BINDTODEVICE sockopt. This does two things:

  • Packets will always egress from the interface you specified, regardless of what the kernel routing tables says.
  • Only packets arriving on the specified interface will be handed to the socket. Packets arriving on other interfaces will not.

If you have multiple interfaces you want to switch between, I'd suggest creating one socket per interface. Because you'll also only receive packets to the interface you've bound to, you'll need to add all of these sockets to your select()/poll()/whatever you use.

#include <net/if.h>

struct ifreq ifr;

memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, "eth1", sizeof(ifr.ifr_name));
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
(void *)&ifr, sizeof(ifr)) < 0) {
perror("SO_BINDTODEVICE failed");
}

(*note)
Bind() to an interface IP address can lead to confusing but nonetheless correct behavior. For example if you bind() to the IP address for eth1, but the routing table sends the packet out eth0, then a packet will appear on the eth0 wire but carrying the source IP address of the eth1 interface. This is weird but allowed, though packets sent back to the eth1 IP address would be routed back to eth1. You can test this using a Linux system with two iP interfaces. I have one, and did test it, and bind() is not effective in steering the packet out a physical interface.

Though technically allowed, depending on topology this may nonetheless not work. To dampen distributed denial of service attacks where the attackers use forged IP source addresses, many routers now perform Reverse Path Forwarding (RPF) checks. Packets with a source IP address on the "wrong" path may be dropped.

Specify which ethernet port to use for UDP writes when all have same IP addresses

You can do this, but not with sockets, or even using the networking protocol stack of your host.

But you can send and receive complete packets from a particular interface, using a mechanism such as winpcap, tun/tap, or slirp. Actually a proper network test needs to do this anyway, because you will need to test the peer's ability to handle malformed packets, which the host networking stack will never generate.

Basic observation, your task is essentially equivalent to implementing bridging in user-mode, although you aren't selecting interface from a bridge learn table, the rest is the same. So take a look at some software that does user-mode bridging on Win32, for example coLinux.

If your requirements document actually says that it will be done using Winsock2, you're going to need to fight to get that changed before you have any hope of progress. (This is why requirements should specify goals, not means.)

How to detect the physical connected state of a network cable/connector?

You want to look at the nodes in


/sys/class/net/

I experimented with mine:

Wire Plugged in:

eth0/carrier:1
eth0/operstate:unknown

Wire Removed:

eth0/carrier:0
eth0/operstate:down

Wire Plugged in Again:

eth0/carrier:1
eth0/operstate:up

Side Trick: harvesting all properties at once the easy way:

grep "" eth0/* 

This forms a nice list of key:value pairs.

Ethernet frames from NIC

If what you want is to bypass the kernel, DPDK in Linux and NetMap in FreeBSD are options to do just that.

Is it possible to choose specific network interface to transmit data in Python?

If we're talking about tcp/udp, then (like in any other langauge) the socket interface allows you to bind a specific ip address to it, so you do that by binding the interface's address.

People have the misconception that binding is only for listening on a socket, but this is also true for connecting, and its just that in normal usage the binding is chosen for you.
Try this:

import socket
s = socket.socket()
s.bind(('192.168.1.111', 0))
s.connect(('www.google.com', 80))

Here we use the ip from interface eth0 (mine is 192.168.1.111) with a system-chosen source port to connect to google's web server. (You can also choose the source port if you want by replacing the 0)

EDIT:
To get the IP address that is used for a specific interface you can use this recipe (linux only) - http://code.activestate.com/recipes/439094-get-the-ip-address-associated-with-a-network-inter/

(if you have multiple IPs on the same interface I'm not sure that it'll work. I assume it will return one of them)

How can I check the data transfer on a network interface in python?

The best way to poll ethernet interface statistics is through SNMP...

  • It looks like you're using linux... if so, load up your snmpd with these options... after installing snmpd, in your /etc/defaults/snmpd (make sure the line with SNMPDOPTS looks like this):

    SNMPDOPTS='-Lsd -Lf /dev/null -u snmp -I -smux,usmConf,iquery,dlmod,diskio,lmSensors,hr_network,snmpEngine,system_mib,at,interface,ifTable,ipAddressTable,ifXTable,ip,cpu,tcpTable,udpTable,ipSystemStatsTable,ip,snmp_mib,tcp,icmp,udp,proc,memory,snmpNotifyTable,inetNetToMediaTable,ipSystemStatsTable,disk -Lsd -p /var/run/snmpd.pid'

  • You might also need to change the ro community to public See Note 1 and set your listening interfaces in /etc/snmp/snmpd.conf (if not on the loopback)...

  • Assuming you have a functional snmpd, at this point, you can poll ifHCInBytes and ifHCOutBytes See Note 2 for your interface(s) in question using this...

poll_bytes.py:

from SNMP import v2Manager
import time

def poll_eth0(manager=None):
# NOTE: 2nd arg to get_index should be a valid ifName value
in_bytes = manager.get_index('ifHCInOctets', 'eth0')
out_bytes = manager.get_index('ifHCOutOctets', 'eth0')
return (time.time(), int(in_bytes), int(out_bytes))

# Prep an SNMP manager object...
mgr = v2Manager('localhost')
mgr.index('ifName')
stats = list()
# Insert condition below, instead of True...
while True:
stats.append(poll_eth0(mgr))
print poll_eth0(mgr)
time.sleep(5)

SNMP.py:

from subprocess import Popen, PIPE
import re

class v2Manager(object):
def __init__(self, addr='127.0.0.1', community='public'):
self.addr = addr
self.community = community
self._index = dict()

def bulkwalk(self, oid='ifName'):
cmd = 'snmpbulkwalk -v 2c -Osq -c %s %s %s' % (self.community,
self.addr, oid)
po = Popen(cmd, shell=True, stdout=PIPE, executable='/bin/bash')
output = po.communicate()[0]
result = dict()
for line in re.split(r'\r*\n', output):
if line.strip()=="":
continue
idx, value = re.split(r'\s+', line, 1)
idx = idx.replace(oid+".", '')
result[idx] = value
return result

def bulkwalk_index(self, oid='ifOutOctets'):
result = dict()
if not (self._index==dict()):
vals = self.bulkwalk(oid=oid)
for key, val in vals.items():
idx = self._index.get(key, None)
if not (idx is None):
result[idx] = val
else:
raise ValueError, "Could not find '%s' in the index (%s)" % self.index
else:
raise ValueError, "Call the index() method before calling bulkwalk_index()"
return result

def get_index(self, oid='ifOutOctets', index=''):
# This method is horribly inefficient... improvement left as exercise for the reader...
if index:
return self.bulkwalk_index().get(index, "<unknown>")
else:
raise ValueError, "Please include an index to get"

def index(self, oid='ifName'):
self._index = self.bulkwalk(oid=oid)

END NOTES:

  1. SNMP v2c uses clear-text authentication. If you are worried about security / someone sniffing your traffic, change your community and restrict queries to your linux machine by source ip address. The perfect world would be to modify the SNMP.py above to use SNMPv3 (which encrypts sensitive data); most people just use a non-public community and restrict snmp queries by source IP.

  2. ifHCInOctets and ifHCOutOctets provide instantaneous values for the number of bytes transferred through the interface. If you are looking for data transfer rate, of course there will be some additional math involved.

Listening to virtual network interface

I just found a tutorial which answers my own question. It was actually really easy using AF_PACKET sockets.

There is a lovely tutorial on microhowto.info, which explains how AF_PACKET sockets work, better than I ever could. It even includes a section "Capture only from a particular network interface".


Here is a minimal example, which worked for my use case:

#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <net/ethernet.h>
#include <linux/if_packet.h>
#include <sys/socket.h>

// [...]

// Create socket
int fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (fd == -1) {
perror("ERROR socket");
exit(1);
}

// Interface index (i.e. obtainable via ioctl SIOCGIFINDEX)
int ifindex = 1337;

// create link layer socket address
struct sockaddr_ll addr = {0};
addr.sll_family = AF_PACKET;
addr.sll_ifindex = ifindex;
addr.sll_protocol = htons(ETH_P_ALL)

if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
perror("ERROR bind");
exit(1);
}

char buffer[65535];
ssize_t len;
do {
len = recv(fd, buffer, sizeof(buffer) -1, 0);
if (len < 0) {
perror("ERROR recvfrom");
exit(1);
}
printf("recived data (length: %i)\n", (int) len);
} while (len > 0);

Get the network interface type in Linux via C program

You might try to use RTNETLINK from Netlink(1,2).
See Manipulating the Networking Environment Using RTNETLINK for an exemple.



Related Topics



Leave a reply



Submit