OS X(MACOS) 上面打开 utun 驱动,并且读写/C++

就不用去看别人乱写一通的文章了,我在这里直接说重点,这东西不是什么NB玩意儿,整那么多花里胡哨没得意义。
 

有需要的童鞋可以自己在MAC虚拟机上、或者MAC真机电脑上面写C/C++程序整整。

在MACOS上苹果在很早之前的版本就内置了 utun 驱动,这个驱动有几个限制,简单明了的说一下大家就懂了。

1、utun 驱动不可以设置为特定的网卡名字,跟LINUX 二点几版本内核集成的 tun 驱动不同。

2、utun 驱动只支持P2P点对点模式,所以设置不了MAC、整不了ARP这些L2的东西。

3、utun 驱动需要明确说明,IP协议类型是AF_INET、还是AF_INET6,就是读入的IP包、写入utun的IP包头前有四个字节来表示。

虽然我是整不懂,烂苹果(Apple)为啥要整四个字节,也许是为了CPU读写更快那么一丝丝(真就一丝丝,笑~)?实际上就它就只用了一个字节,其它三个字节都是0、别问撒、自己看AF_INET、AF_INET6 宏定义的数值,跟IP协议头上,PROTO占多少位就晓得。

所以它的结构就是(注意:苹果是小端机,低位在前,高位在后,但是网络字节序是大端,就编程高位在前,低位在后)

所以就变成这样了:

0 0 0 0 IP头 IP载荷

4、utun 驱动通过 utunnum(就是 sockaddr_ctl::sc_unit)来管理使用那个 utun,没有就创建,这个不可以是0,最少就1。

所以:utun0 就等于 utunnum = 0 + 1

如果写1出来就是 utun0、如果写2就是 utun1,需要说明一点在比较有历史遗留感的 MAC OSX系统上面,这个最大就是255,所以你只能在 0~255 之间重试,后头可以整到42亿了,但是最好就0~255。

5、在 macos 上面操作 utun 的IP、MASK、GW这块,用命令行来整,ifconfig 可以办到,或者用 networksetup 命令,因为OS X API C/C++接口不是撒系统都能用,动不动就失败,用命令行去配置兼容性要好的多,除了有点慢,但是执行成功与否都是可以取得的,成功返回0,否则返回非0。

废话不多说,直接上代码:

#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include           // ioctl
#include    // struct socketaddr_ctl
#include         // UTUN_CONTROL_NAME
#include 

#include 
#include 
#include 

#include 
#include 
#include 
#include 

#include 
#include 

        bool utun_set_cloexec(int fd) noexcept
        {
            int flags = fcntl(fd, F_GETFD, 0);
            if (flags == -1)
            {
                return false;
            }

            flags |= FD_CLOEXEC;
            if (fcntl(fd, F_SETFD, flags) < 0)
            {
                return false;
            }

            return true;
        }


        /* Helper functions that tries to open utun device
         * return -2 on early initialization failures (utun not supported
         * at all (old OS X) and -1 on initlization failure of utun
         * device (utun works but utunX is already used */
        int utun_open(int utunnum) noexcept
        {
            if (utunnum < 0 || utunnum > UINT8_MAX)
            {
                return -1;
            }

            struct ctl_info ctlInfo;
            memset(&ctlInfo, 0, sizeof(ctlInfo));
            strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name));

            int fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
            if (fd == -1)
            {
                return fd;
            }

            if (ioctl(fd, CTLIOCGINFO, &ctlInfo) < 0)
            {
                close(fd);
                return -1;
            }

            struct sockaddr_ctl sc;
            memset(&sc, 0, sizeof(sc));

            sc.sc_id = ctlInfo.ctl_id;
            sc.sc_len = sizeof(sc);
            sc.sc_family = AF_SYSTEM;
            sc.ss_sysaddr = AF_SYS_CONTROL;
            sc.sc_unit = utunnum + 1;

            if (connect(fd, (struct sockaddr*)&sc, sizeof(sc)) < 0)
            {
                close(fd);
                return -1;
            }

            int flags = fcntl(fd, F_GETFL, 0);
            if (flags == -1)
            {
                close(fd);
                return -1;
            }
            
            if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
            {
                close(fd);
                return -1;
            }
            
            utun_set_cloexec(fd);
            return fd;
        }

        bool utun_get_if_name(int tun, ppp::string& ifrName) noexcept
        {
            ifrName.clear();
            if (tun == -1)
            {
                return false;
            }

            /* Retrieve the assigned interface name. */
            char utunname[1000];
            socklen_t utunname_len = sizeof(utunname);
            if (getsockopt(tun, SYSPROTO_CONTROL, UTUN_OPT_IFNAME, utunname, &utunname_len) < 0)
            {
                return false;
            }

            ifrName = utunname;
            return true;
        }

        bool utun_set_if_ip_gw_and_mask(int tun, const ppp::string& ip, const ppp::string& gw, const ppp::string& mask) noexcept
        {
            if (tun == -1 || ip.empty() || mask.empty())
            {
                return false;
            }

            ppp::string name;
            if (!utun_get_if_name(tun, name))
            {
                return false;
            }

            char cmd[1000];
            snprintf(cmd, sizeof(cmd), "ifconfig %s inet %s %s netmask %s up", name.data(), ip.data(), gw.data(), mask.data());

            int status = system(cmd);
            return status == 0;
        }


        bool utun_set_mtu(int tun, int mtu) noexcept
        {
            if (tun == -1)
            {
                return false;
            }

            // MTU: 68 ~ 65535 RANGE.
            if (mtu < 68)
            {
                mtu = ITap::Mtu;
            }

            ppp::string name;
            if (!utun_get_if_name(tun, name))
            {
                return false;
            }

            char buf[1000];
            snprintf(buf, sizeof(buf), "ifconfig %s mtu %d > /dev/null 2>&1", name.data(), mtu);

            int status = system(buf);
            return status == 0;
        }

        int utun_utunnum(const ppp::string& dev) noexcept
        {
            int v = 0;
            if (dev.empty())
            {
                return v;
            }

            ppp::string s;
            for (char ch : dev)
            {
                if (ch >= '0' && ch <= '9')
                {
                    s.append(1, ch);
                    continue;
                }
            }

            v = atoi(s.data());
            if (v < 0)
            {
                v = 0;
            }
            elif(v > UINT8_MAX)
            {
                v = UINT8_MAX;
            }

            return v;
        }

参考资料,可以看这:

// https://stackoverflow.com/questions/2300149/how-can-i-determine-the-default-gateway-on-iphone/8095530#8095530

// https://stackoverflow.com/questions/5390164/getting-routing-table-on-macosx-programmatically/11265543#11265543

// https://bugzilla.mozilla.org/show_bug.cgi?id=1579424

// https://gist.github.com/etodd/d8184b91c02306b889c13eb03f81fb6d

// https://github.com/songgao/water/issues/3#issuecomment-158704536

// https://build.open.net/doxygen/route_8c_source.html

// https://github.com/OpenVPN/open/blob/master/src/open/tun.c#L3250

读入的包;

            // According to the low-level interface documentation of the operating system (obscure location), 
            // OSX utun packets need to add four bytes to the frame header to indicate the AF_INET and AF_INET6 protocol types.
            int packet_length = e.PacketLength;
            if (packet_length > sizeof(uint32_t))
            {
                Byte* packet = (Byte*)e.Packet;
                Byte protocol = (Byte)ntohl(*(uint32_t*)packet);
                if (protocol == AF_INET)
                {
                    e.Packet = packet + sizeof(uint32_t);
                    e.PacketLength = packet_length - sizeof(uint32_t);
                    
// 你的代码。
                }
            }

 

写入的包:

            // Check whether the IP packet protocol output by the VPN protocol stack is AF_INET.
            struct ip_hdr* iphdr = (struct ip_hdr*)packet;
            if (ip_hdr::IPH_V(iphdr) != ip_hdr::IP_VER) // IP_VER = 4
            {
                return false;
            }

            // Copy to the current thread or coroutine stack, reduce memory fragmentation and write to the kernel, 
            // Discarding the report if it exceeds the MTU size.
            if (packet_size > 1500) // MTU=1500
            {
                return false;
            }
            else
            {
                Byte chunk[1500 + sizeof(uint32_t)];
                size_t chunk_size = packet_size + sizeof(uint32_t);

                *(uint32_t*)chunk = htonl(AF_INET);
                memcpy(chunk + sizeof(uint32_t), packet, packet_size);

                ssize_t bytes_transferred = ::write(tun, chunk, chunk_size);
                return bytes_transferred > -1;
            }

你可能感兴趣的:(C/C++,macos)