UDP数据包转发从sendmsg获取EINVAL

时间:2017-10-27 22:45:57

标签: linux networking dns udp embedded-linux

我正在编写自己的DNS黑洞来阻止家庭网络上的广告和恶意软件。我意识到这种类型的程序已经存在,但我想学习这个过程并编写自己的程序。从Linux文档中可以看出,sendmsg()可以被告知使用不同的返回地址,以便可以转发UDP数据包,接收服务器将响应发送给原始请求者而不是我的服务器。从稀疏文档中,我设置了绑定到端口53(DNS)的套接字。我正在接收DNS请求,解释它们并在网站被列入黑名单时做出响应。对于“好”的域名,我得到2个不同的结果。在MacOS上,我的转发请求已发送,但响应将返回到我的程序而不是原始请求者。在Linux(Armbian 4.13内核)上,我从sendmsg()调用中获取EINVAL并且没有发送任何内容。有人能说清楚我做错了吗? (为简洁起见,已删除所有错误检查)

其他信息...此方案中的一个问题是原始DNS请求绑定到53以外的套接字端口(显然)。如何告诉sendmsg()将响应返回到原始请求的正确端口号?

我的“代理”版本有效。我存储了原始请求的交易ID和请求端口号,并且能够成功返回响应

最新消息 - 我能够成功完成我想要的,但不能使用sendmsg()。通过使用RAW套接字并“欺骗”IP头中的返回地址,我能够将数据包发送回不同的返回地址和端口号(使用sendto)。 nslookup认为这是一个问题,但浏览器可以使用它。我想我们可以称之为“案件结案”

struct sockaddr_in addr, addrfrom, fwaddr;
socklen_t addrLen = sizeof(struct sockaddr_in);
int rc, listen_sock;

listen_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
rc = 1;
setsockopt(listen_sock, IPPROTO_IP, IP_PKTINFO, &rc, sizeof(rc));
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(53); // port address of DNS
addr.sin_addr.s_addr = INADDR_ANY;
bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr));
memset(&fwaddr, 0, sizeof(fwaddr));
fwaddr.sin_family = AF_INET;
fwaddr.sin_port = htons(53);
fwaddr.sin_addr.s_addr = 0x08080808; // Google DNS server

<... Receive DNS request from client...>

if (bForward)
{
struct msghdr msg;
struct iovec iov[1];
struct {
   struct cmsghdr cm; /* this ensures alignment */
   struct in_pktinfo ipi;
} cmsg;
memset(&cmsg, 0, sizeof(cmsg));
memset(&msg, 0, sizeof(msg));
iov[0].iov_base = request_bufffer;
iov[0].iov_len = request_len;
msg.msg_flags = 0;
msg.msg_name = &fwaddr; // dest address of packet
msg.msg_namelen = addrLen;
msg.msg_iov = &iov[0];
msg.msg_iovlen = 1;

msg.msg_control = &cmsg;
msg.msg_controllen = sizeof(cmsg); //sizeof(struct in_pktinfo);
cmsg.cm.cmsg_len = sizeof(cmsg);
cmsg.cm.cmsg_level = IPPROTO_IP;
cmsg.cm.cmsg_type = IP_PKTINFO;

cmsg.ipi.ipi_ifindex = 0;
cmsg.ipi.ipi_spec_dst = addrfrom.sin_addr; // original source address
rc= sendmsg(listen_sock, &msg, 0);
} // bForward

1 个答案:

答案 0 :(得分:3)

问题

我在此代码中看到了2个潜在问题。

  1. 您没有使用CMSG macros来分配和操作您的辅助数据(即您的cmsg结构)。
  2. 即使修复了这个问题,您也会尝试欺骗已发送数据包的源IP地址 - 即您尝试声明它来自不属于此框的地址。我从来没有尝试过这样做,但如果发现内核安全性阻止你这样做,就不会感到惊讶。
  3. 那该怎么办?

    首先,我会使用像this SO answer这样的代码来修复你的meassage结构。我怀疑你应该密切关注in_pktinfo的偏移量和你传递给cmsg_len的长度,因为这些可能会导致EINVAL错误。一种可能的策略可能是将代码切换回正常发送数据包以查看哪些数据结构被拒绝的程度。

    如果没有这样做,我想你可以考虑重新编写你的发送逻辑,以使用原始套接字绕过内核可能正在进行的任何IP验证。例如,this code看起来应该大致是您需要的。

    如果仍然不够,我怀疑你需要在内核安全系统中进行路由。