I'm testing a 4G modem with different SIM cards, and with one of them, I'm having the following output from ifconfig ppp0:
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500
inet 10.168.34.88 netmask 255.255.255.255 destination 10.168.34.88
ppp txqueuelen 3 (Point-to-Point Protocol)
RX packets 1873 bytes 1490197 (1.4 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 1679 bytes 214990 (209.9 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
I'm a bit troubled by the fact that local IP address is the same as the destination IP address (10.168.34.88). (I would expect destination to be the address of the peer).
By the way, this is confirmed by the output of pppd, which says:
May 31 12:39:50 bouli9 pppd[1223384]: CHAP authentication succeeded: Welcome!
May 31 12:39:50 bouli9 pppd[1223384]: CHAP authentication succeeded
May 31 12:39:52 bouli9 pppd[1223384]: Protocol-Reject for unsupported protocol 0xff03
May 31 12:39:52 bouli9 pppd[1223384]: local IP address 10.168.34.88
May 31 12:39:52 bouli9 pppd[1223384]: remote IP address 10.168.34.88
I have access to internet using ppp0 anyway, but still, I'm a bit curious on what's going on here.
I'm trying to send raw UDP datagrams using a raw IP socket on Linux (I specify both IP and UDP headers).
Should I check the mmsghdr.msg_len field after sendmmsg? This field designates a number of bytes of a datagram sent, but since I send IP datagrams I think it should always equal to a length of a whole datagram (or not?).
Notes
I construct a UDP datagram completely by myself. Only one UDP packet (UDP header + data) is encapsulated into an IP datagram.
how do I sniff packets that are only outbound packets?
I tried to sniff only destination port but it doesn't succeed at all
Quite easy indeed:
mac = get_if_hwaddr(conf.iface).lower()
sniff(lfilter=lambda pkt:pkt.haslayer(Ether) and pkt[Ether].src.lower()==mac)
If you want to print them:
sniff(lfilter=lambda pkt:pkt.haslayer(Ether) and pkt[Ether].src.lower()==get_if_hwaddr(conf.iface).lower(), prn=lambda x:x.summary())
You may use any other interface than conf.iface
Maybe you can get your device MAC address and filter any packets with that address as source address.
ifconfig 1.2.3.4 mtu 1492
This will set MTU to 1492 for incoming, outgoing packets or both? I think it is only for incoming
TLDR: Both. It will only transmit packets with a payload length less than or equal to that size. Similarly, it will only accept packets with a payload length within your MTU. If a device sends a larger packet, it should respond with an ICMP unreachable (oversized) message.
The nitty gritty:
Tuning the MTU for your device is useful because other hops between you and your destination may encapsulate your packet in another form (for example, a VPN or PPPoE.) This layer around your packet results in a bigger packet being sent along the wire. If this new, larger packet exceeds the maximum size of the layer, then the packet will be split into multiple packets (in a perfect world) or will be dropped entirely (in the real world.)
As a practical example, consider having a computer connected over ethernet to an ADSL modem that speaks PPPoE to an ISP. Ethernet allows for a 1500 byte payload, of which 8 bytes will be used by PPPoE. Now we're down to 1492 bytes that can be delivered in a single packet to your ISP. If you were to send a full-size ethernet payload of 1500 bytes, it would get "fragmented" by your router and split into two packets (one with a 1492 byte payload, the other with an 8 byte payload.)
The problem comes when you want to send more data over this connection - lets say you wanted to send 3000 bytes: your computer would split this up based on your MTU - in this case, two packets of 1500 bytes each, and send them to your ADSL modem which would then split them up so that it can fulfill its MTU. Now your 3000 byte data has been fragmented into four packets: two with a payload of 1492 bytes and two with a payload of 8 bytes. This is obviously inefficient, we really only need three packets to send this data. Had your computer been configured with the correct MTU for the network, it would have sent this as three packets in the first place (two 1492 byte packets and one 16 byte packet.)
To avoid this inefficiency, many IP stacks flip a bit in the IP header called "Don't Fragment." In this case, we would have sent our first 1500 byte packet to the ADSL modem and it would have rejected the packet, replying with an Internet Control (ICMP) message informing us that our packet is too large. We then would have retried the transmission with a smaller packet. This is called Path MTU discovery. Similarly, a layer below, at the TCP layer, another factor in avoiding fragmentation is the MSS (Maximum Segment Size) option where both hosts reply with the maximum size packet they can transfer without fragmenting. This is typically computed from the MTU.
The problem here arises when misconfigured firewalls drop all ICMP traffic. When you connect to (say) a web server, you build a TCP session and send that you're willing to accept TCP packets based on your 1500 byte MTU (since you're connected over ethernet to your router.) If the foreign web server wanted to send you a lot of data, they would split this into chunks that (when combined with the TCP and IP headers) came out to 1500 byte payloads and send them to you. Your ISP would receive one of these and then try to wrap it into a PPPoE packet to send to your ADSL modem, but it would be too large to send. So it would reply with an ICMP unreachable, which would (in a perfect world) cause the remote computer to downsize its MSS for the connection and retransmit. If there was a broken firewall in the way, however, this ICMP message would never be reached by the foreign web server and this packet would never make it to you.
Ultimately setting your MTU on your ethernet device is desirable to send the right size frames to your ADSL modem (to avoid it asking you to retransmit with a smaller frame), but it's critical to influence the MSS size you send to remote hosts when building TCP connections.
ifconfig ... mtu <value> sets the MTU for layer2 payloads sent out the interface, and will reject larger layer2 payloads received on this interface. You must ensure your MTU matches on both sides of an ethernet link; you should not have mismatched mtu values anywhere in the same ethernet broadcast domain. Note that the ethernet headers are not included in the MTU you are setting.
Also, ifconfig has not been maintained in linux for ages and is old and deprecated; sadly linux distributions still include it because they're afraid of breaking old scripts. This has the very negative effect of encouraging people to continue using it. You should be using the iproute2 family of commands:
[mpenning#hotcoffee ~]$ sudo ip link set mtu 1492 eth0
[mpenning#hotcoffee ~]$ ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1492 qdisc mq state UP qlen 1000
link/ether 00:1e:c9:cd:46:c8 brd ff:ff:ff:ff:ff:ff
[mpenning#hotcoffee ~]$
Large incoming packets may be dropped based on the interface MTU size.
For example, the default MTU 1500 on
Linux 2.6 CentOS (tested with Ethernet controller: Intel Corporation 80003ES2LAN Gigabit Ethernet Controller (Copper) (rev 01))
drops Jumbo packets >1504. Errors appear in ifconfig and there are rx_long_length_errors indications for this in ethtool -S output.
Increasing MTU indicates Jumbo packets should be supported.
The threshold for when to drop packets based on their size being too large appears to depend on MTU (-4096, -8192, etc.)
Oren
It's the Maximum Transmission Unit, so it definitely sets the outgoing maximum packet size. I'm not sure if will reject incoming packets larger than the MTU.
There is no doubt that MTU configured by ifconfig impacts Tx ip fragmentation, I have no more comments.
But for Rx direction, I find whether the parameter impacts incoming IP packets, it depends. Different manufacturer behaves differently.
I tested all the devices on hand and found 3 cases below.
Test case:
Device0 eth0 (192.168.225.1, mtu 2000)<--ETH cable-->Device1 eth0
(192.168.225.34, mtu MTU_SIZE)
On Device0 ping 192.168.225.34 -s ICMP_SIZE,
Checking how MTU_SIZE impacts Rx of Device1.
case 1:
Device1 = Linux 4.4.0 with Intel I218-LM:
When MTU_SIZE=1500, ping succeeds at ICMP_SIZE=1476, fails at ICMP_SIZE=1477 and above. It seems that there is a PRACTICAL MTU=1504 (20B(IP header)+8B(ICMP header)+1476B(ICMP data)).
When MTU_SIZE=1490, ping succeeds at ICMP_SIZE=1476, fails at ICMP_SIZE=1477 and above, behave the same as MTU_SIZE=1500.
When MTU_SIZE=1501, ping succeeds at ICMP_SIZE=1476, 1478, 1600, 1900. It seems that jumbo frame is switched on once MTU_SIZE is set >1500 and there is no 1504 restriction any more.
case 2:
Device1 = Linux 3.18.31 with Qualcomm Atheros AR8151 v2.0 Gigabit Ethernet:
When MTU_SIZE=1500, ping succeeds at ICMP_SIZE=1476, fails at ICMP_SIZE=1477 and above.
When MTU_SIZE=1490, ping succeeds at ICMP_SIZE=1466, fails at ICMP_SIZE=1467 and above.
When MTU_SIZE=1501, ping succeeds at ICMP_SIZE=1477, fails at ICMP_SIZE=1478 and above.
When MTU_SIZE=500, ping succeeds at ICMP_SIZE=476, fails at ICMP_SIZE=477 and above.
When MTU_SIZE=1900, ping succeeds at ICMP_SIZE=1876, fails at ICMP_SIZE=1877 and above.
This case behaves exactly as Edward Thomson said, except that in my test the PRACTICAL MTU=MTU_SIZE+4.
case 3:
Device1 = Linux 4.4.50 with Raspberry Pi 2 Module B ETH:
When MTU_SIZE=1500, ping succeeds at ICMP_SIZE=1472, fails at ICMP_SIZE=1473 and above. So there is a PRACTICAL MTU=1500 (20B(IP header)+8B(ICMP header)+1472B(ICMP data)) working there.
When MTU_SIZE=1490, behave the same as MTU_SIZE=1500.
When MTU_SIZE=1501, behave the same as MTU_SIZE=1500.
When MTU_SIZE=2000, behave the same as MTU_SIZE=1500.
When MTU_SIZE=500, behave the same as MTU_SIZE=1500.
This case behaves exactly as Ron Maupin said in Why MTU configuration doesn't take effect on receiving direction?.
To sum it all, in real world, after you set ifconfig mtu,
sometimes the Rx IP packts get dropped when exceed 1504 , no matter what MTU value you set (except that the jumbo frame is enabled).
sometimes the Rx IP packts get dropped when exceed the MTU+4 you set on receiving device.
sometimes the Rx IP packts get dropped when exceed 1500, no matter what MTU value you set.
... ...
For some specific networking tests, I've created a VLAN device, eth1.900, and a couple of aliases, eth1.900:1 and eth1.900.2.
eth1.900 Link encap:Ethernet HWaddr 00:18:E7:17:2F:13
inet addr:1.0.1.120 Bcast:1.0.1.255 Mask:255.255.255.0
eth1.900:1 Link encap:Ethernet HWaddr 00:18:E7:17:2F:13
inet addr:1.0.1.200 Bcast:1.0.1.255 Mask:255.255.255.0
eth1.900:2 Link encap:Ethernet HWaddr 00:18:E7:17:2F:13
inet addr:1.0.1.201 Bcast:1.0.1.255 Mask:255.255.255.0
When connecting to a server, is there a way to specify which of these aliases will be used? I can ping using the -I <ip> address option to select which alias to use, but I can't see how to do it with a TCP socket in code without using raw sockets, since I would also like to run without extra socket privileges, i.e. not running as root, if possible.
Unfortunately, even with root, SO_BINDTODEVICE doesn't work because the alias device name is not recognized:
printf("Bind to %s\n", devname);
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (char*)devname, sizeof(devname)) != 0)
{
perror("SO_BINDTODEVICE");
return 1;
}
Output:
Bind to eth1.900:1
SO_BINDTODEVICE: No such device
Use getifaddrs() to enumerate all the interfaces and find the IP address for the interface you want to bind to. Then use bind() to bind to that IP address, before you call connect().
Since a packet can't be send out on an aliased interface anyway, it would make no sense to use SO_BINDTODEVICE on one. SO_BINDTODEVICE controls which device a packet is sent out from if routing cannot be used for this purpose (for example, if it's a raw Ethernet frame).
You don't show the definition of devname, but if it's a string pointer, e.g.:
char *devname = "eth1.900:1";
Then perhaps it's failing since you specify the argument size using sizeof devname, which would in this case be the same as sizeof (char *), i.e. typically 4 on a 32-bit system.
If setsockopt() expects to see the actual size of the argument, i.e. the length of the string, this could explain the issue since it's then perhaps just inspecting the first four characters and failing since the result is an invalid interface name.