ICMP sockets (linux) - linux

Is it possible to use ICMP sockets under the IP protocol? Maybe something like:
socket(PF_INET, <type>, IPPROTO_ICMP)?
What should I put in the <type> field? I saw some examples using SOCK_RAW, but won't that prevent the OS from doing his job handling the IP protocol?
And another thing. How can the OS know to which process he should send the ICMP datagrams, since there are no ports involved with the protocol?

Linux have a special ICMP socket type you can use with:
socket(PF_INET, SOCK_DGRAM, IPPROTO_ICMP);
This allows you to only send ICMP echo requests The kernel will handle it specially (match request/responses, fill in the checksum).
This only works if a special sysctl is set. By default not even root can use this kind of socket. You specify the user groups that can access it. To allow root (group 0) to use ICMP sockets, do:
sysctl -w net.ipv4.ping_group_range="0 0"
Here is an example program to demonstrate the very basic usage of sending an ICMP echo request:
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <sys/select.h>
//note, to allow root to use icmp sockets, run:
//sysctl -w net.ipv4.ping_group_range="0 0"
void ping_it(struct in_addr *dst)
{
struct icmphdr icmp_hdr;
struct sockaddr_in addr;
int sequence = 0;
int sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP);
if (sock < 0) {
perror("socket");
return ;
}
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_addr = *dst;
memset(&icmp_hdr, 0, sizeof icmp_hdr);
icmp_hdr.type = ICMP_ECHO;
icmp_hdr.un.echo.id = 1234;//arbitrary id
for (;;) {
unsigned char data[2048];
int rc;
struct timeval timeout = {3, 0}; //wait max 3 seconds for a reply
fd_set read_set;
socklen_t slen;
struct icmphdr rcv_hdr;
icmp_hdr.un.echo.sequence = sequence++;
memcpy(data, &icmp_hdr, sizeof icmp_hdr);
memcpy(data + sizeof icmp_hdr, "hello", 5); //icmp payload
rc = sendto(sock, data, sizeof icmp_hdr + 5,
0, (struct sockaddr*)&addr, sizeof addr);
if (rc <= 0) {
perror("Sendto");
break;
}
puts("Sent ICMP\n");
memset(&read_set, 0, sizeof read_set);
FD_SET(sock, &read_set);
//wait for a reply with a timeout
rc = select(sock + 1, &read_set, NULL, NULL, &timeout);
if (rc == 0) {
puts("Got no reply\n");
continue;
} else if (rc < 0) {
perror("Select");
break;
}
//we don't care about the sender address in this example..
slen = 0;
rc = recvfrom(sock, data, sizeof data, 0, NULL, &slen);
if (rc <= 0) {
perror("recvfrom");
break;
} else if (rc < sizeof rcv_hdr) {
printf("Error, got short ICMP packet, %d bytes\n", rc);
break;
}
memcpy(&rcv_hdr, data, sizeof rcv_hdr);
if (rcv_hdr.type == ICMP_ECHOREPLY) {
printf("ICMP Reply, id=0x%x, sequence = 0x%x\n",
icmp_hdr.un.echo.id, icmp_hdr.un.echo.sequence);
} else {
printf("Got ICMP packet with type 0x%x ?!?\n", rcv_hdr.type);
}
}
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("usage: %s destination_ip\n", argv[0]);
return 1;
}
struct in_addr dst;
if (inet_aton(argv[1], &dst) == 0) {
perror("inet_aton");
printf("%s isn't a valid IP address\n", argv[1]);
return 1;
}
ping_it(&dst);
return 0;
}
Note that the kernel will reject and fail the sendto() call if the data sent does not have room for a proper ICMP header, and the ICMP type must be 8 (ICMP_ECHO) and the ICMP code must be 0.

Yes it is possible, since the ping command does ICMP.
To find out the syscalls involved, you can strace that command (under root).
You could also glance into that command's source code, e.g. Debian's ping
And there is the liboping library to help you...

Related

UDP broadcasting on same machine?

I wrote a broadcaster and a listener which recvfrom a particular port on the system. I used REUSEADDR option for the socket in listener, to make multiple instances of listener monitor the same port on same system.
When I run the listener on different machines, and send packet from another machine, all the listeners receives the packet. but when I run multiple instances of listener on same machine and if I try sending udp packets, only the first instance of listener gets the packet not all. I want to broadcast UDP packets on same machine and want all the listeners to receive the packet. I am on linux.
I followed the Beej's Guide.
Edit 01
listener code
/*
** listener.c -- a datagram sockets "server" demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define MYPORT "4950" // the port users will be connecting to
#define MAXBUFLEN 100
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(int argc, char *argv[])
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
int reuse_addr = 1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("listener: socket");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
if(errno == EADDRINUSE)
{
if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&reuse_addr, sizeof reuse_addr) < 0)
perror("setsockopt(): REUSEADDR\n"),exit(1);
}
else
{
close(sockfd);
perror("listener: bind");
continue;
}
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
return 2;
}
freeaddrinfo(servinfo);
printf("listener: waiting to recvfrom...\n");
addr_len = sizeof their_addr;
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
printf("listener: got packet from %s\n",
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s));
printf("listener: packet is %d bytes long\n", numbytes);
buf[numbytes] = '\0';
printf("listener: packet contains \"%s\"\n", buf);
close(sockfd);
return 0;
}
Set socket option to SO_REUSEPORT in linstener.
SO_REUSEPORT - socket option allows multiple sockets on the same host to bind to the same port
Make accept_local as 1 using echo in SYSFS.
/proc/sys/net/ipv4/conf/all # cat accept_local
1
This will allow local communication.

Unix Domain Sockets: accept() not setting sun_path

I am doing some testing with Unix domain sockets and I can communicate over them without an issue, however, when I call accept() on the server side of my test program, the returned struct sockaddr_un doesn't contain a sun_path.
I am pretty sure that Inet sockets have their address and port properly filled out after an accept() call, so am I doing something wrong in my test program or am I expecting the wrong outcome?
I am running CentOS 6.2 and gcc 4.4.6.
Sample Code:
server.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NAME "socket"
int main(int argc, char **argv)
{
int sock, msgsock, rval;
struct sockaddr_un server, client;
char buf[1024];
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, NAME);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) {
perror("binding stream socket");
exit(1);
}
printf("Socket has name %s\n", server.sun_path);
listen(sock, 5);
for (;;) {
socklen_t len = sizeof(client);
msgsock = accept(sock, (struct sockaddr *)&client, &len);
if (msgsock == -1)
perror("accept");
else do {
printf("strlen(sun_path) = %zu\n", strlen(client.sun_path));
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
}
close(sock);
unlink(NAME);
return 0;
}
client.c
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define DATA "Half a league, half a league . . ."
int main(int argc, char **argv)
{
int sock;
struct sockaddr_un server;
if (argc < 2) {
printf("usage:%s <pathname>", argv[0]);
exit(1);
}
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, argv[1]);
if (connect(sock, (struct sockaddr *) &server,
sizeof(struct sockaddr_un)) < 0) {
close(sock);
perror("connecting stream socket");
exit(1);
}
if (write(sock, DATA, sizeof(DATA)) < 0)
perror("writing on stream socket");
close(sock);
return 0;
}
Just to reiterate the question:
Why isn't sun_path filled out after the accept() call on the server?
I am really not sure if this is an answer at all. Probably it's more like musings about some research, though maybe still worth while reading.
The value filled by accept(2) seems to be quite protocol agnostic at least in Linux 3.16.0, NetBSD 6.1.4 and Darwin 13.1.0 kernels. In practice this means that the second parameter to accept(2), struct sockaddr * gets filled only up to what is shared between all protocols. So what you have in your hands after a successful acccept(2) is far from being a complete struct sockaddr_un.
Probably nobody thought it would be of much importance at the time first implementations of accept(2) were done and now we're stuck with this. Fortunately there is a way around that, in case one has lost the pathname used for socket with call to bind(2), and would now like to find it again.
With struct sockaddr_storage and getsockname(2) the member sun_path is accessible. So, to make sure you are getting all juicy details, call getsockname(2) after a successful call to accept(2) (this would be put after line number 40 in your server.c):
struct sockaddr_storage ss;
socklen_t sslen = sizeof(struct sockaddr_storage);
if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) {
struct sockaddr_un *un = (struct sockaddr_un *)&ss;
printf("socket name is: %s\n", un->sun_path);
}
Or just use this:
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define NAME "socket"
int main(int argc, char **argv)
{
int sock, msgsock, rval;
struct sockaddr_un server, client;
char buf[1024];
sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening stream socket");
exit(1);
}
server.sun_family = AF_UNIX;
strcpy(server.sun_path, NAME);
if (bind(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un))) {
perror("binding stream socket");
exit(1);
}
printf("Socket has name %s\n", server.sun_path);
listen(sock, 5);
for (;;) {
socklen_t len = sizeof(client);
msgsock = accept(sock, (struct sockaddr *)&client, &len);
if (msgsock == -1)
perror("accept");
else do {
printf("strlen(sun_path) = %zu\n", strlen(client.sun_path));
struct sockaddr_storage ss;
socklen_t sslen = sizeof(struct sockaddr_storage);
if (getsockname(msgsock, (struct sockaddr *)&ss, &sslen) == 0) {
struct sockaddr_un *un = (struct sockaddr_un *)&ss;
printf("socket name is: %s\n", un->sun_path);
}
bzero(buf, sizeof(buf));
if ((rval = read(msgsock, buf, 1024)) < 0)
perror("reading stream message");
else if (rval == 0)
printf("Ending connection\n");
else
printf("-->%s\n", buf);
} while (rval > 0);
close(msgsock);
}
close(sock);
unlink(NAME);
return 0;
}
This has been tested, ie. it compiles and produces expected results, to work on a GNU/Linux system running kernel 3.16.0, a NetBSD system running 6.1.4 kernel, and a system equipped with OS/X Mavericks, running 13.1.0 kernel. In all of these behaviour of accept(2) is consistent: sun_path is nowhere to be found in the structure filled. The behaviour of getsockname(2) is consistent between different operating environments too, making all protocol specific details available.
You haven't bound your client socket to an address.
You don't need to bind your client socket to an address for connect() to work; but, if you expect to access your client address in your server, then you do have to bind().
Make sense?
Just setup a call to bind() before you connect in your client. Make sure you the path you use for you client is valid and check for errors as normal.

setsockopt on "accepted" fd on Linux

I have had a rather strange observation about behavior of setsockopt on Linux for SO_REUSEADDR. In one line: if I apply the sockopt to an fd returned by accept on a "listening socket" the socketoption is reflected on the port held by the listening socket.
Ok some code.
Server : Opens a socket, applies SO_REUSEADDR to be true. Accepts a connection and then applies SO_REUSEADDR to be false on the fd on the fd returned by accept.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
int main(void)
{
int s, len;
int sin_size;
int reuse = 1;
int ret;
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
my_addr.sin_port = htons(33235);
if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error\n");
return -1;
}
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if( bind(s, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) < 0)
{
printf("Bind Error\n");
return -1;
}
listen(s, 6);
reuse = 0;
memset(&my_addr, 0, sizeof(my_addr));
while(1) {
ret = accept(s, (struct sockaddr*)&my_addr, &len);
if (ret<0) {
printf("Accept failed\n");
} else {
printf("Accepted a client setting reuse add to 0\n");
setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
}
}
printf("Server exiting\n");
return 0;
}
Client : Client connects to the server, and doesn't do anything after that ensuring that the server socket stays in TIME_WAIT state.
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <errno.h>
int main(void)
{
int s, len;
int sin_size;
struct sockaddr_in my_addr;
memset(&my_addr, 0, sizeof(my_addr));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
my_addr.sin_port = htons(33235);
if( (s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket Error\n");
return -1;
}
if (!connect(s,(struct sockaddr*)&my_addr, sizeof(struct sockaddr)))
{
printf("Client Connected successfully\n");
}
else
{
printf("%s\n",strerror(errno));
}
while(1) sleep(1);
return 0;
}
Steps that I do reproduce the issue.
Run server.
Connect client.
Kill and restart server. The server fails with Bind Failure
I tested this on mac os. And the bind didn't fail. I have digged up all Posix specifications and none of them say that this code is undefined.
Question:
Can someone with more experience on this share their understanding of the issue?
One way to think about it is that SO_REUSEADDR determines if you can have another socket bound to that same address. It's a property of any socket (listen or connection), but very commonly inherited from listen via accept. In linux it's mapped to the struct sock "sk_reuse" flag.
If you clear this flag on a FD you "accepted" then from that point on the IP/Port pair is considered busy-and-non-reusable. The SO_REUSEADDR flag on the listen socket does not change, but the flag on the accepted socket affects bind logic. You could probably check this with getsockopt.
If you want to know more you can try to read the inet_csk_get_port function: http://lxr.free-electrons.com/source/net/ipv4/inet_connection_sock.c#L100. This is where the actual "binding" takes place.

How to log every ICMP reply message

There is this question i ran into, how can we find the ip address of the ICMP reply message in GNU/Linux?
Look into libpcap - it's a very efficient library for network sniffing, which lets you capture exactly the type of packets you specify (possibly filtered even further by source/destination address etc.). You can then parse the packet and extract the source and destination IP addresses. The linked page has documentation and several tutorials.
Note that you need to be doing the capture on a computer across which the traffic passes (source, destination, or anything in between) as in modern Ethernet networks (connected with switches), you normally don't see all network traffic. See this Q&A from Wireshark (which is essentially a GUI to libpcap) for possible workarounds.
You can try IPTables logging.
This is a simple loop (C Linux) to intercept all ICMP REQUEST/REPLY:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char **argv)
{
int sock;
int optval;
int ret;
int addrlen;
struct sockaddr_in sIn;
char *buffer;
char *sAddr;
char *dAddr;
int proto;
int type;
buffer = malloc(sizeof(char) * 32);
sAddr = malloc(sizeof(char) * 16);
dAddr = malloc(sizeof(char) * 16);
if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) != -1) {
setsockopt(sock, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));
sIn.sin_family = AF_INET;
sIn.sin_addr.s_addr = INADDR_ANY;
addrlen = sizeof(sIn);
memset(buffer, 0, 32);
while ((ret = recvfrom(sock, buffer, 31, 0, (struct sockaddr *)&sIn, &addrlen)) != -1) {
if (ret > 20) {
proto = (unsigned char)buffer[9];
type = (unsigned char)buffer[20];
if (proto == 1 && (type == 8 || type == 0)) {
memset(sAddr, 0, 16);
memset(dAddr, 0, 16);
sprintf(sAddr, "%d.%d.%d.%d",
(unsigned char)buffer[12],
(unsigned char)buffer[13],
(unsigned char)buffer[14],
(unsigned char)buffer[15]);
sprintf(dAddr, "%d.%d.%d.%d",
(unsigned char)buffer[16],
(unsigned char)buffer[17],
(unsigned char)buffer[18],
(unsigned char)buffer[19]);
if (type == 8)
fprintf(stdout, "-> ICMP REQUEST FROM %s TO %s\n", sAddr, dAddr);
else
fprintf(stdout, "<- ICMP REPLY FROM %s TO %s\n", sAddr, dAddr);
}
}
memset(buffer, 0, 32);
}
close(sock);
}
free(buffer);
free(sAddr);
free(dAddr);
return 0;
}
Enjoy ;)
You can certainly open a socket with
socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)
And log the source address of packets arriving. You will need to know the structure of an ICMP datagram for this to work. See man 7 raw

Is raw socket on loopback interface possible?

We are trying to communicate with the server listening on Linux loopback interface via raw socket and it seems like the server does not get a single packet from us. The packets we send are visible in Wireshark.
Is raw socket on loopback possible at all? (Please, don't ask why we need it: it's too complicated to explain here)
EDIT: this is how we open it
_I_RawSocket = socket( PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)))
memset( &ifr, 0, sizeof( ifr ) );
strcpy( ifr.ifr_ifrn.ifrn_name, _InterfaceName);
ioctl( _I_RawSocket, SIOCGIFINDEX, &ifr )
memset( &sll, 0, sizeof( sll ) );
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons( ETH_P_ALL );
bind( _I_RawSocket, (struct sockaddr *) &sll, sizeof( sll ))
The server is lighttpd and it's reachable via normal socket on localhost.
netstat --raw prints empty table but I'm absolutely sure we have two functional raw sockets on normal eth devices.
Raw sockets behave particularly fizzy with bind() and connect(), but I can't confirm that your issue lies with them. I suggest you follow a more straightforward approach:
Sender
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define DEST "127.0.0.1"
int main(int argc, char **argv)
{
int s;
struct sockaddr_in dst_addr;
char packet[50];
struct iphdr *ip = (struct iphdr *)packet;
if((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
dst_addr.sin_family = AF_INET;
dst_addr.sin_port = 0; /* not needed in SOCK_RAW */
inet_pton(AF_INET, DEST, (struct in_addr *)&dst_addr.sin_addr.s_addr);
memset(dst_addr.sin_zero, 0, sizeof(dst_addr.sin_zero));
memset(packet, 'A', sizeof(packet)); /* payload will be all As */
ip->ihl = 5;
ip->version = 4;
ip->tos = 0;
ip->tot_len = htons(40);
ip->frag_off = 0; /* NF */
ip->ttl = 64;
ip->protocol = IPPROTO_RAW; /* this has to be IPPROTO_RAW */
ip->check = 0;
ip->saddr = dst_addr.sin_addr.s_addr;
ip->daddr = dst_addr.sin_addr.s_addr;
while(42) {
sleep(5);
if (sendto(s, packet, sizeof(packet), 0,
(struct sockaddr *)&dst_addr, (socklen_t)sizeof(dst_addr)) < 0)
perror("uh oh:");
}
return(0);
}
Receiver
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int s;
struct sockaddr_in src_addr;
char packet[50];
if ((s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror("error:");
exit(EXIT_FAILURE);
}
memset(packet, 0, sizeof(packet));
socklen_t *len = (socklen_t *)sizeof(src_addr);
int fromlen = sizeof(src_addr);
while(42) {
if (recvfrom(s, &packet, sizeof(packet), 0,
(struct sockaddr *)&src_addr, &fromlen) < 0)
perror("uh oh:");
int i = sizeof(struct iphdr); /* print the payload */
for(; i < sizeof(packet); i++) {
printf("%c", packet[i]);
}
printf("\n");
}
return(0);
}
I hope these behave exactly like you want them to. Read man 7 raw for the gory details of why this works and more importantly man 7 packet if you want to extend it. Also, take note that IPPROTO_RAW implies the IP_HDRINCL socket option, which is why we're constructing the ip header ourselves - although the IP checksum and total length are computed and filled in by the kernel, still.
edit: In addition, if you wanted a raw socket with which to send valid data to an application like lighttpd, you'd have to match the protocol argument to socket() as well as provide valid values for the IP header fields. A proper ethernet header is not mandatory - the only important field will be filled for you by the kernel stack.
Please make sure bind to if_index
if (ioctl(sock, SIOCGIFINDEX, &stEthReq ) < 0 )
{
printf( "failed to get IF index!" );
return -1;
}
memset(&client_addr, 0, sizeof(client_addr));
client_addr.sll_family = AF_PACKET;
client_addr.sll_ifindex = stEthReq.ifr_ifru.ifru_ivalue;
client_addr.sll_protocol = VOS_HTONS(usEthType);
ret = bind(sock,(struct sockaddr *)(&client_addr), sizeof(client_addr));

Resources