Linux's iptable and iproute allows us to mark packets and matches the mark later (fwmark), allowing for great flexibility in configuring routes and firewalls.
Is there a way to set those marks while sending the packet from a C program, either via ordinary sockets interface or via specific linux system calls?
I found the SO_MARK socket option in socket(7) man page:
SO_MARK (since Linux 2.6.25)
Set the mark for each packet sent through this socket (similar
to the netfilter MARK target but socket-based). Changing the
mark can be used for mark-based routing without netfilter or
for packet filtering. Setting this option requires the
CAP_NET_ADMIN capability.
It is not per packet, as I originally asked, suits my purpose. You can set it with setsockopt():
int fwmark;
//fwmark = <some value>;
if (setsockopt(sockfd, SOL_SOCKET, SO_MARK, &fwmark, sizeof fwmark) == -1)
perror("failed setting mark for socket packets");
Related
I'm messing with sockets in C and this protocol continues to show up, I couldn't find anything about it, so what is it used for? What's the difference between HOPOPT and IP?
Also i'm don't get why the last argument of the socket() function should be 0. According to the man page:
The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0. However, it is possible that many protocols may exist, in which case a particular protocol must be specified in this manner. The protocol number to use is specific to the “communication domain” in which communication is to take place; see protocols(5). See getprotoent(3) on how to map protocol name strings to protocol numbers.
As far as I understand setting the last argument to 0 will let the standard library to decide which protocol to use but in which case would one use a number other than 0?
HOPOPT is the acronym of the Hop-by-Hop IPv6 extension header. It is a header that allows to add even more options to an IPv6 packet. It is normal that IPv6 packets include this header.
socket() is the system call that BSD and others (Linux et al.) provide to create a new socket, that is the internal representation of a network connection. When creating a new socket, the desired protocol must be specified: TCP, UDP, etc., which may go over IPv4, IPv6, etc.
The paragraph that you are citing explains that one or many protocols may exist for each socket type.
If only one exists, the protocol argument must be zero. For instance, SOCK_STREAM sockets are only implemented by TCP:
int sk = socket(AF_INET, SOCK_STREAM, 0);
If more exist, than you must specify which protocol in particular you want to use. For example, the SOCK_SEQPACKET type can be implemented with the SCTP protocol:
int sk = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
So, in conclusion:
If you want to create a socket, choose what protocol to use, for instance TCP over IPv4.
HOPOPT is totally normal in an IPv6 packet. If you see it appear in your traces, because you created an IPv6 socket (using AF_INET6), it is OK.
I want to receive ethernet packets from socket in Linux, but only those, which have one of two custom Ethtype values.
As I know, if only 1 ethtype should be received, it's possible to specify this value while creating socket like this
int socket = socket(PF_PACKET, SOCK_RAW, htons(ETHERTYPE_CUSTOM_1);
But what if I have 2 different ethtypes? Should I use 2 sockets or write some custom filter? Or is there some any simple way?
Create two sockets, one for each ethertype. Then you can use select() or epoll() to wait for packets on either socket at the same time.
I think you should use libpcap library. You need to access bpf packer filter.
This is easy one.
Or you can use iptables rules an netfilter library. You need to set prerouting iptables rules to forward all packet to specific port and your application binding this port as listening mode and you can receive full packet.
I'm sending some ping packets via a raw socket in C, on my linux machine.
int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
This means that I specify the IP packet header when I write to the socket (IP_HDRINCL is implied).
Writing to the socket with send fails, telling me I need to specify an address.
If I use sendto then it works. For sendto I must specify a sockaddr_in struct to use, which includes the fields sin_family, sin_port and sin_addr.
However, I have noticed a few things:
The sin_family is AF_INET - which was already specified when the socket was created.
The sin_port is naturally unused (ports are not a concept for IP).
It doesn't matter what address I use, so long as it is an external address (the IP packet specifies 8.8.8.8 and the sin_addr specifies 1.1.1.1).
It seems none of the extra fields in sendto are actually used to great extent. So, is there a technical reason why I have to use sendto instead of send or is it just an oversight in the API?
Writing to the socket with send fails, telling me I need to specify an address.
It fails, because the send() function can only be used on connected sockets (as stated here). Usually you would use send() for TCP communication (connection-oriented) and sendto() can be used to send UDP datagrams (connectionless).
Since you want to send "ping" packets, or more correctly ICMP datagrams, which are clearly connectionless, you have to use the sendto() function.
It seems none of the extra fields in sendto are actually used to great
extent. So, is there a technical reason why I have to use sendto
instead of send or is it just an oversight in the API?
Short answer:
When you are not allowed to use send(), then there is only one option left, called sendto().
Long answer:
It is not just an oversight in the API. If you want to send a UDP datagram by using an ordinary socket (e.g. SOCK_DGRAM), sendto() needs the information about the destination address and port, which you provided in the struct sockaddr_in, right? The kernel will insert that information into the resulting IP header, since the struct sockaddr_in is the only place where you specified who the receiver will be. Or in other words: in this case the kernel has to take the destination info from your struct as you don't provide an additional IP header.
Because sendto() is not only used for UDP but also raw sockets, it has to be a more or less "generic" function which can cover all the different use cases, even when some parameters like the port number are not relevant/used in the end.
For instance, by using IPPROTO_RAW (which automatically implies IP_HDRINCL), you show your intention that you want to create the IP header on your own. Thus the last two arguments of sendto() are actually redundant information, because they're already included in the data buffer you pass to sendto() as the second argument. Note that, even when you use IP_HDRINCL with your raw socket, the kernel will fill in the source address and checksum of your IP datagram if you set the corresponding fields to 0.
If you want to write your own ping program, you could also change the last argument in your socket() function from IPPROTO_RAW to IPPROTO_ICMP and let the kernel create the IP header for you, so you have one thing less to worry about. Now you can easily see how the two sendto()-parameters *dest_addr and addrlen become significant again because it's the only place where you provide a destination address.
The language and APIs are very old and have grown over time. Some APIs can look weird from todays perspective but you can't change the old interfaces without breaking a huge amount of existing code. Sometimes you just have to get used to things that were defined/designed many years or decades ago.
Hope that answers your question.
The send() call is used when the sockets are in a TCP SOCK_STREAM connected state.
From the man page:
the send() call may be used only when the socket is in a connected
state (so that the intended recipient is known).
Since your application obviously does not connect with any other socket, we cannot expect send() to work.
In addition to InvertedHeli's answer, the dest_addr passed in sendto() will be used by kernel to determine which network interface to used.
For example, if dest_addr has ip 127.0.0.1 and the raw packet has dest address 8.8.8.8, your packet will still be routed to the lo interface.
Context
Studying Berkeley packet filter on Linux Debian 64 bits to filter the packets received by the opened socket.
I use AF_PACKET so i manage even the layer 2 of packets.
So far it works beautifully. But i have to filter every packet on every socket and it is not efficient. Hence I use BPF.
Question
Since I have my applications set the filters by themselves with
setsockopt(sd, SOL_PACKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) < 0 )
I would like to know :
if the kernel will filter and direct the packets to the right socket (filtering happens once per packet on the system at the kernel level)
or
if the kernel will send all the packets as before and bpf will take filter in every socket (each packet will be analyzed + filtered as many times as there are open sockets on the system because every application will see the packet coming <-> promiscuous mode. This is not efficient).
I am not sure.
Thanks
Negro - but the question shows a fundamental misunderstanding of AF_PACKET socket vs. promiscuous mode and I would like to outline that using BPF filters on AF_PACKET sockets in LINUX is implemented in a efficient way (for the usual use-case).
About the general issue with the question:
Using an AF_PACKET socket does not mean that the NIC is switched to
promiscuous mode - it just forwards all frames directed to the
host to the user space (so filter based on L2 address is still applied - in contrast to a NIC in promiscuous mode that happily ignores a non-matching destination-MAC). This should relax your question at all as the usual frame/packet distribution process is applied even if there is an AF_PACKET socket.
About efficiency:
Only AF_PACKET sockets will see all frames directed to the host. A filter attached to a socket is evaluated per socket. There is no central spot in the kernel that handles all the filters and distributes the frames to its direction. Usually an AF_PACKET socket is used to implement a protocol(handler) in user space. Therefore those old wise guys who implemented AF_PACKET assumed that most frames directed to an AF_PACKET socket will be filtered/dropped cause the user would like to handle only a very specific subset of the frames.
The filter is applied on a socket buffer (skb - a container that holds the frame and its associated control/status data) that is shared by all entities taking part in the frame processing. Only if the filter matches a clone of this buffer is created and handed over to the user. There is even a comment on top of the function responsible for processing the skb in context of a AF_PACKET socket that says:
* This function makes lazy skb cloning in hope that most of packets
* are discarded by BPF.
For further information on packet filter on AF_PACKET sockets see the kernel doc for network filter.
The bpf program will sit in the kernel. It will process the data going to the particular socket identified in the setsockopt call. If a particular packet passes the filter, it will get delivered, else it is filtered out.
I mean to emphasize that two parallel invocations of the API with different socket do not affect the other, and should work correctly.
Regarding internal implementation in the kernel, I am not sure.
tx
I am new to socket programming
I saw a ICMP request program , in that they used setsockopt to a socket
int on = 1;
setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on))
but even if I do not use this statement, the program runs correctly. Why is it so
important to mention to the kernel this socket including the IP structure?
The IP_HDRINCL option does the following (from the man page):
The IPv4 layer generates an IP header when sending a packet unless the IP_HDRINCL socket option is enabled on the socket. When it is enabled, the packet must contain an IP header. For receiving the IP header is always included in the packet.
Presumably your program is constructing an IP header. If you remove this option, it will use the kernel's IP header. Whether that 'works' or not depends on what your program does. Perhaps under some circumstances it wants to customise the IP header and with this removed that will not work.
If you post the rest of the program or tell us a bit about it, we might be able to help.