user2215771
user2215771

Reputation: 239

Dynamically changing the source IP address

I have a server with a couple of different IP addresses. At this point, each IP can recieve a UDP request, but it is always the same IP that replies, which the requesters do not like.

To make a long story short, this is all the essential code:

int sock;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
memset((char*)&serv_addr, 0, sizeof(serv_addr));

sock = socket(AF_INET, SOCK_DGRAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);
...
recvfrom(sock, buffer, BUFLEN, 0, (struct sockaddr *)&cli_addr, &clilen);
...
sendto(sock, resData, resLen, 0, (struct sockaddr *)&cli_addr, sizeof(cli_addr));

I would like to be able to specify somehow which IP is used to send back my packet (and this could differ for each and every request), but I have no idea how and if this even can be accomplished using sockets. I'm not really that experienced in this field, so all the help that I can get is greatly appreciated.

Edit below

I have found a potential solution in the accepted answer here: How to re bind a udp socket in Linux

However, a new problem emerges. How do I know which IP/interface recieved the request? So that I can respond using that IP/interface.

Upvotes: 1

Views: 5661

Answers (3)

user2215771
user2215771

Reputation: 239

I have solved my issue, and in good manner the solution must be posted! I did not use SOCK_RAW, nor did I bind my interface to a local IP or the such. This is a mixture of 100 pages on google and some stackoverflow, so I'm a bit sad that I haven't saved the links to give out the correct credits.

There might be obvious flaws in the code as I am no expert, however this is the solution I came up with and it works. I just started cleaning up the code (it gets messy when you try stuff from 100 different pages and combine them). Anyways, here it is:

Receive part:

int sock;
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
char buffer[BUFLEN];

// Next
memset((char*)&serv_addr, 0, sizeof(serv_addr));
sock = socket(AF_INET, SOCK_DGRAM, 0);
serv_addr.sin_family = AF_INET;
//serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(PORT);

clilen = sizeof(cli_addr);

if (bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
    error("ERROR on binding");
}

bool opt = true;
setsockopt(sock, IPPROTO_IP, IP_PKTINFO, (char *)&opt, sizeof(opt));

// Packet data will be saved in buffer
bzero(buffer, BUFLEN);
struct iovec vector;
vector.iov_base = buffer;
vector.iov_len = sizeof(buffer);

struct msghdr msg;
msg.msg_name = &cli_addr;
msg.msg_namelen = sizeof(cli_addr);
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
int flags = 0;

// Not sure what controlBuffer contains at this point
char controlBuffer[1024];
msg.msg_control = controlBuffer;
msg.msg_controllen = 1024;

// Recv packet
int bytes = ::recvmsg(sock, &msg, flags);

struct cmsghdr *cmsg;
struct in_pktinfo *dest_ip_ptr;
int dest_ip = 0;

// Loop through IP header messages
cmsg = CMSG_FIRSTHDR(&msg);
for ( cmsg = CMSG_FIRSTHDR(&msg);
    cmsg != NULL;
    cmsg = CMSG_NXTHDR( &msg, cmsg ) )
{
    if (cmsg->cmsg_level != IPPROTO_IP ||
        cmsg->cmsg_type != IP_PKTINFO)
    {
        continue;
    }

    // Get IP (int)
    struct in_pktinfo *dest_ip_ptr = (struct in_pktinfo*)CMSG_DATA(cmsg);
    dest_ip = dest_ip_ptr->ipi_addr.s_addr;
}

// Format IP
unsigned char ipParts[4];
ipParts[0] = dest_ip & 0xFF;
ipParts[1] = (dest_ip >> 8) & 0xFF;
ipParts[2] = (dest_ip >> 16) & 0xFF;
ipParts[3] = (dest_ip >> 24) & 0xFF;

Send part:

// Build source sockaddr
struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(struct sockaddr_in));
src_addr.sin_family = AF_INET;

// Save IP into a char array
char destIp[16];
memset(destIp, 0, sizeof(destIp));
sprintf(destIp, "%d.%d.%d.%d", ipParts[0], ipParts[1], ipParts[2], ipParts[3]);
inet_aton(destIp, &(src_addr.sin_addr));

char cmbuf[CMSG_SPACE(sizeof(struct in_pktinfo))];

bzero(buffer, BUFLEN);
int len = dp.getRaw(buffer);

struct msghdr mh;
memset(&mh, 0, sizeof(mh));

struct cmsghdr *cmsg_send;
struct in_pktinfo *pktinfo;

struct iovec iov[1];
iov[0].iov_base = buffer;
iov[0].iov_len = BUFLEN;

mh.msg_name = &cli_addr; // destination address of packet
mh.msg_namelen = sizeof(cli_addr);
mh.msg_control = cmbuf;
mh.msg_controllen = sizeof(cmbuf);
mh.msg_flags = 0;
mh.msg_iov = iov;
mh.msg_iovlen = 1;

// after initializing msghdr & control data to CMSG_SPACE(sizeof(struct in_pktinfo))
cmsg_send = CMSG_FIRSTHDR(&mh);
cmsg_send->cmsg_level = IPPROTO_IP;
cmsg_send->cmsg_type = IP_PKTINFO;
cmsg_send->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
pktinfo = (struct in_pktinfo*) CMSG_DATA(cmsg_send);
pktinfo->ipi_ifindex = 0;
pktinfo->ipi_spec_dst = src_addr.sin_addr;

int rc = sendmsg(sock, &mh, 0);

Upvotes: 1

Salgar
Salgar

Reputation: 7775

You need to bind() to a local IP address, the IP of the interface that you want to use.

Read this guide about bind()

Beej's Guide To Networking#bind

Upvotes: 1

UserM
UserM

Reputation: 320

Typical TCP/UDP sockets binds on a particular IP/Port before doing Rx/Tx. Try using raw socket for your approach.

its rather big code, please refer the following link http://sock-raw.org/papers/sock_raw

create a raw socket - Rx via raw socket -now you will receive entire frame from L2 Tx via raw socket - transmit from L3 header after modifying relevant fields

Upvotes: 0

Related Questions