From cbf4ff2ad5e686d226a91c062cec077b0115f4d3 Mon Sep 17 00:00:00 2001 From: "Morgan 'ARR\\!' Allen" Date: Fri, 5 Jan 2024 13:12:09 -0800 Subject: [PATCH] still quite broken but a good starting point --- CMakeLists.txt | 9 +++ README.md | 33 ++++++++ include/ifnow.h | 113 ++++++++++++++++++++++++++ include/ifnow_now.h | 28 +++++++ include/ifnow_tun.h | 26 ++++++ src/main.c | 89 +++++++++++++++++++++ src/now.c | 189 ++++++++++++++++++++++++++++++++++++++++++++ src/tun.c | 174 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 661 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 README.md create mode 100644 include/ifnow.h create mode 100644 include/ifnow_now.h create mode 100644 include/ifnow_tun.h create mode 100644 src/main.c create mode 100644 src/now.c create mode 100644 src/tun.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0c0aad9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.18) + +project('ifname') + +add_executable(ifnow src/main.c src/tun.c src/now.c) + +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -ggdb") +target_include_directories(ifnow PUBLIC ./include/) +set_target_properties(ifnow PROPERTIES LINK_LIBRARIES -pthread) diff --git a/README.md b/README.md new file mode 100644 index 0000000..98ef2a9 --- /dev/null +++ b/README.md @@ -0,0 +1,33 @@ +# ifnow +A Linux network interface device for communication over the ESP-Now wireless broadcast protocol. + +NOTE: This is EXTREMELY work in progress and as it requires root permissions is not recommended to be run unmonitored. + +# Build +``` +git clone https://git.oit.cloud/morgan/ifnow.git +cd ifnow +mkdir build +cd build +cmake ../ +make +``` + +This should result in the `ifnow` binary in the build directory. + +# Usage +This is two steps, first setting up monitoring mode (using aircrack-ng in this method). You will need to determine +your wireless interface name and replace if different from `wlp2s0` in the example. You will also want to set the +channel to match the ESP32 configuration. + +``` +sudo airmon-ng start wlp2s0 +sudo iw wlp2s0 set channel 1 +./ifnow wlp2s0mon now1 10.10.0.13 +``` + +The interface is now up as `now1` and you should be able to write to this interface. In theory `pcap` can +be used to intercept raw packets but if used with LwIP on the ESP32 end, which can be accomplished with +[esp_netif_now](https://git.oit.cloud/morgan/esp_netif_now), any IP stack should work. Has been testing +with UDP and some [CoAP](https://libcoap.net). There are definitely still issues with parsing some incoming +packets. diff --git a/include/ifnow.h b/include/ifnow.h new file mode 100644 index 0000000..5245e0a --- /dev/null +++ b/include/ifnow.h @@ -0,0 +1,113 @@ +#ifndef __IFNOW_H__ +#define __IFNOW_H__ + +#define IFNOW_MAX_RECEIVE (512) +#define DATARATE_1Mbps 0x02 +#define DATARATE_2Mbps 0x04 +#define DATARATE_6Mbps 0x0c +#define DATARATE_9Mbps 0x12 +#define DATARATE_12Mbps 0x18 +#define DATARATE_18Mbps 0x24 +#define DATARATE_24Mbps 0x30 +#define DATARATE_36Mbps 0x48 +#define DATARATE_48Mbps 0x60 +#define DATARATE_54Mbps 0x6c + +#define CHANNEL_freq_1 2412 +#define CHANNEL_freq_2 2417 +#define CHANNEL_freq_3 2422 +#define CHANNEL_freq_4 2427 +#define CHANNEL_freq_5 2432 +#define CHANNEL_freq_6 2437 +#define CHANNEL_freq_7 2442 +#define CHANNEL_freq_8 2447 +#define CHANNEL_freq_9 2452 +#define CHANNEL_freq_10 2457 +#define CHANNEL_freq_11 2462 +#define CHANNEL_freq_12 2467 +#define CHANNEL_freq_13 2472 + +#define WLAN_LEN 24 +#define ACTIONFRAME_HEADER_LEN 8 +#define VENDORSPECIFIC_CONTENT_LEN 7 + +struct IEEE80211_radiotap { + uint8_t version; + uint8_t pad; + uint16_t length; + uint32_t present; + uint8_t flags; + uint8_t datarate; + uint16_t channel_freq; + uint16_t channel_flags_quarter; +}; + +#define IFNOW_RADIOTAP_DEFAULT() {\ + .version = 0,\ + .pad = 0,\ + .length = sizeof(struct IEEE80211_radiotap),\ + .present = 0x0000000e,\ + .flags = 0x10,\ + .datarate = DATARATE_6Mbps,\ + .channel_freq = CHANNEL_freq_1,\ + .channel_flags_quarter = 0x00c0,\ +} + +struct IEEE80211_vendorspecific { + uint8_t elementID; + uint8_t length; + uint8_t OUI[3]; + uint8_t type; + uint8_t version; + uint8_t payload[250]; +} __attribute__((__packed__)); + +#define IFNOW_VENDORSPECIFIC_DEFAULT() {\ + .elementID = 0xdd,\ + .OUI[0] = 0x18,\ + .OUI[1] = 0xfe,\ + .OUI[2] = 0x34,\ + .type = 0x04,\ + .version = 0x01,\ +} + +struct IEEE80211_actionframe { + uint8_t category_code; + uint8_t OUI[3]; + uint8_t unknown_bytes[4]; + struct IEEE80211_vendorspecific content; +} __attribute__((__packed__)); + +#define IFNOW_ACTIONFRAME_DEFAULT() {\ + .category_code = 0x7f,\ + .OUI[0] = 0x18,\ + .OUI[1] = 0xfe,\ + .OUI[2] = 0x34,\ +} + +struct IEEE80211_wlan { + uint8_t type; + uint8_t flags; + uint16_t duration; + uint8_t da[6]; + uint8_t sa[6]; + uint8_t bssid[6]; + uint16_t seq; + struct IEEE80211_actionframe actionframe; + uint32_t fcs; +}; + +#define IFNOW_WLAN_DEFAULT() {\ + .type = 0xd0,\ + .flags = 0x00,\ + .duration = 0x0000,\ + .seq = 0x0280,\ + .fcs = 0x00000000,\ +} + +typedef struct { + struct IEEE80211_radiotap radiotap; + struct IEEE80211_wlan wlan; +} ifnow_packet_t; + +#endif // __IFNOW_H__ diff --git a/include/ifnow_now.h b/include/ifnow_now.h new file mode 100644 index 0000000..8972300 --- /dev/null +++ b/include/ifnow_now.h @@ -0,0 +1,28 @@ +#ifndef __IFNOW_WLAN_H__ +#define __IFNOW_WLAN_H__ + +#include +#include +#include + +#include "ifnow.h" + +#define ERR_SOCKET_FAILED (-1) +#define ERR_IOCTL_FAILED (-2) +#define ERR_BIND_FAILED (-3) +#define ERR_FILTER_FAILED (-4) + +typedef int (*ifnow_now_receive_cb)(void *now_cfg, uint8_t *buf, uint32_t len); + +typedef struct { + int fd_if_socket; + char *if_name; + struct sock_fprog filter; + pthread_t thread_id; + ifnow_now_receive_cb receive; +} ifnow_now_cfg_t; + +int ifnow_now_init(ifnow_now_cfg_t *now_cfg); +int ifnow_now_send(ifnow_now_cfg_t *now_cfg, uint8_t *buf, uint16_t len); + +#endif diff --git a/include/ifnow_tun.h b/include/ifnow_tun.h new file mode 100644 index 0000000..de0b9a5 --- /dev/null +++ b/include/ifnow_tun.h @@ -0,0 +1,26 @@ +#ifndef __IFNOW_TUN_H__ +#define __IFNOW_TUN_H__ + +#include +#include +#include +#include + +#include "ifnow.h" + +typedef int (*ifnow_tun_receive_cb)(void *tun_cfg, uint8_t *buf, uint32_t len); + +typedef struct { + int fd_tuntap; + int fd_netlink; + struct in_addr addr; + char *addr_s; + char *if_name; + pthread_t thread_id; + ifnow_tun_receive_cb receive; +} ifnow_tun_cfg_t; + +int ifnow_tun_init(ifnow_tun_cfg_t *tun_cfg); +int ifnow_tun_send(ifnow_tun_cfg_t *tun_cfg, uint8_t *buf, uint8_t len); + +#endif diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..7760aee --- /dev/null +++ b/src/main.c @@ -0,0 +1,89 @@ +#include +#include +#include + +#include "ifnow.h" +#include "ifnow_now.h" +#include "ifnow_tun.h" + +ifnow_now_cfg_t now_cfg; +ifnow_tun_cfg_t tun_cfg; + +#define ANSI_FONT_COL_RESET "\x1b[0m" +#define FONT_COL_CUSTOM_RED "\e[38;2;200;0;0m" +#define FONT_COL_CUSTOM_GREEN "\e[38;2;0;200;0m" +#define FONT_COL_CUSTOM_BLUE "\e[38;2;0;0;200m" +#define BCKGRD_COL_CUSTOM_RED "\e[48;2;200;0;0m" +#define BCKGRD_COL_CUSTOM_GREEN "\e[48;2;0;200;0m" +#define BCKGRD_COL_CUSTOM_BLUE "\e[48;2;0;0;200m" + +char *color_chart[] = { + // NOW header + BCKGRD_COL_CUSTOM_GREEN, // category version + FONT_COL_CUSTOM_BLUE, FONT_COL_CUSTOM_BLUE, FONT_COL_CUSTOM_BLUE, // org identifier + FONT_COL_CUSTOM_RED, FONT_COL_CUSTOM_RED, FONT_COL_CUSTOM_RED, FONT_COL_CUSTOM_RED, // random + // Vendor Specific Header + BCKGRD_COL_CUSTOM_GREEN, // Element ID + BCKGRD_COL_CUSTOM_BLUE, // Length + FONT_COL_CUSTOM_BLUE, FONT_COL_CUSTOM_BLUE, FONT_COL_CUSTOM_BLUE, // org identifier + BCKGRD_COL_CUSTOM_BLUE, // Type + BCKGRD_COL_CUSTOM_GREEN, // Version + // Body + NULL +}; +uint8_t color_size = 15; + +int ifnow_now_receive_callback(void *cfg, uint8_t *buf, uint32_t len) { + ifnow_now_cfg_t *now_cfg = cfg; + + printf("NOW receive_count: %d\n", len); + for(uint16_t i = 0; i < len; i++) { + printf("%s%02X"ANSI_FONT_COL_RESET" ", (i < color_size ? color_chart[i] : (buf[i] == 0x11 ? BCKGRD_COL_CUSTOM_RED : "")), buf[i]); + if(i % 16 == 15) printf("\n"); + } + printf("\n"); + + // TODO apply buf to actual structs of headers + uint8_t bp = 57; + int err = ifnow_tun_send(&tun_cfg, buf + bp, len - bp); + if(err < 0) { + perror("Failed to send over tunnel"); + } +} + +int ifnow_tun_receive_callback(void *cfg, uint8_t *buf, uint32_t len) { + printf("TUN receive_count: %d\n", len); + + for(uint32_t i = 0; i < len; i++) { + printf("%02X ", buf[i]); + if(i % 16 == 15) printf("\n"); + } + printf("\n"); + + int err = ifnow_now_send(&now_cfg, buf, len); + if(err < 0) { + perror("Failed to send over now"); + } +} + +uint8_t main(int argc, char **argv) { + // TODO handle arguments + // * wifi interface to listen for ESP-Now packets on + // Interface should be in monitor mode + // * name of new TUN interface + // * IP address for `now` interface + + now_cfg.if_name = argv[1]; + now_cfg.receive = &ifnow_now_receive_callback; + + tun_cfg.if_name = argv[2]; + tun_cfg.addr_s = argv[3]; + tun_cfg.receive = &ifnow_tun_receive_callback; + + ifnow_now_init(&now_cfg); + ifnow_tun_init(&tun_cfg); + + while(1) {}; + + return 0; +} diff --git a/src/now.c b/src/now.c new file mode 100644 index 0000000..36f7e6d --- /dev/null +++ b/src/now.c @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ifnow.h" +#include "ifnow_now.h" + +ifnow_packet_t packet; + +int ifnow_now_send(ifnow_now_cfg_t *now_cfg, uint8_t *buf, uint16_t len) { + struct IEEE80211_radiotap radiotap = IFNOW_RADIOTAP_DEFAULT(); + struct IEEE80211_actionframe action = IFNOW_ACTIONFRAME_DEFAULT(); + struct IEEE80211_vendorspecific vendor = IFNOW_VENDORSPECIFIC_DEFAULT(); + struct IEEE80211_wlan wlan = IFNOW_WLAN_DEFAULT(); + + memcpy(&packet.radiotap, &radiotap, sizeof(struct IEEE80211_radiotap)); + memcpy(&packet.wlan.actionframe, &action, sizeof(struct IEEE80211_actionframe)); + + memcpy(&packet.wlan.actionframe.content, &vendor, sizeof(struct IEEE80211_vendorspecific)); + memcpy(&packet.wlan.actionframe.content.payload, buf, len < 250 ? len : 250); + packet.wlan.actionframe.content.length = len + 5; + + memcpy(&packet.wlan.da, buf, 6); + memcpy(&packet.wlan.sa, buf + 6, 6); + memcpy(&packet.wlan.bssid, buf, 6); + + printf("NOW: sending %d bytes\n", sizeof(packet)); + + for(uint16_t i = 0; i < sizeof(packet); i++) { + printf("%02X ", ((uint8_t*)&packet)[i]); + if(i % 16 == 15) printf("\n"); + } + printf("\n"); + // TODO verify sendto vs send vs write + int ret = sendto(now_cfg->fd_if_socket, &packet, sizeof(packet), 0, NULL, 0); + + return ret; +} + +void *ifnow_thread_handle(void *arg) { + ifnow_now_cfg_t *now_cfg = arg; + + printf("starting ifnow_now thread.\n"); + + ssize_t receive_count = 0; + uint8_t receive_buffer[IFNOW_MAX_RECEIVE]; + + while(1) { + receive_count = recv(now_cfg->fd_if_socket, receive_buffer, IFNOW_MAX_RECEIVE, MSG_TRUNC); + + if(receive_count > 0 && now_cfg->receive != NULL) { + now_cfg->receive(now_cfg, receive_buffer, receive_count); + } + } +} + +int ifnow_now_init(ifnow_now_cfg_t *now_cfg) { + printf("Using interface: %s\n", now_cfg->if_name); + + struct sockaddr_ll if_bind_addr; + struct ifreq ifr; + + now_cfg->fd_if_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + + if(now_cfg->fd_if_socket == -1) { + perror("Failed to initialize IF socket."); + + return ERR_SOCKET_FAILED; + } + + strncpy((char*)ifr.ifr_name, now_cfg->if_name, IFNAMSIZ); + + int ioctrl_err = ioctl(now_cfg->fd_if_socket, SIOCGIFINDEX, &ifr); + + if(ioctrl_err < 0) { + perror("Failed to set io ctl on IF socket."); + return ERR_IOCTL_FAILED; + } + + if_bind_addr.sll_family = PF_PACKET; + if_bind_addr.sll_protocol = htons(ETH_P_ALL); + if_bind_addr.sll_ifindex = ifr.ifr_ifindex; + + int bind_err = bind(now_cfg->fd_if_socket, (struct sockaddr *)&if_bind_addr, sizeof(if_bind_addr)); + + if(bind_err < 0) { + perror("Failed bind raw socket."); + + return ERR_BIND_FAILED; + } + + now_cfg->filter.len = 53; + + uint32_t MSB_dst = 0; + uint32_t LSB_dst = 0; + + uint32_t MSB_src = 0; + uint32_t LSB_src = 0; + + uint8_t jeq_dst = 0x30; + uint8_t jeq_src = 0x30; + + struct sock_filter temp_code[53] = { + { 0x30, 0, 0, 0x00000003 }, + { 0x64, 0, 0, 0x00000008 }, + { 0x7, 0, 0, 0x00000000 }, + { 0x30, 0, 0, 0x00000002 }, + { 0x4c, 0, 0, 0x00000000 }, + { 0x2, 0, 0, 0x00000000 }, + { 0x7, 0, 0, 0x00000000 }, + { 0x50, 0, 0, 0x00000000 }, + { 0x54, 0, 0, 0x000000fc }, + { 0x15, 0, 42, 0x000000d0 }, + { 0x40, 0, 0, 0x00000018 }, + { 0x15, 0, 40, 0x7f18fe34 }, + { 0x50, 0, 0, 0x00000020 }, + { 0x15, 0, 38, 0x000000dd }, + { 0x40, 0, 0, 0x00000021 }, + { 0x54, 0, 0, 0x00ffffff }, + { 0x15, 0, 35, 0x0018fe34 }, + { 0x50, 0, 0, 0x00000025 }, + { 0x15, 0, 33, 0x00000004 }, + { 0x50, 0, 0, 0x00000000 }, + { 0x45, 31, 0, 0x00000004 }, + { 0x45, 0, 21, 0x00000008 }, + { 0x50, 0, 0, 0x00000001 }, + { 0x45, 0, 4, 0x00000001 }, + { 0x40, 0, 0, 0x00000012 }, + { jeq_dst, 0, 26, LSB_dst }, + { 0x48, 0, 0, 0x00000010 }, + { jeq_dst, 4, 24, MSB_dst }, + { 0x40, 0, 0, 0x00000006 }, + { jeq_dst, 0, 22, LSB_dst }, + { 0x48, 0, 0, 0x00000004 }, + { jeq_dst, 0, 20, MSB_dst }, + { 0x50, 0, 0, 0x00000001 }, + { 0x45, 0, 13, 0x00000002 }, + { 0x45, 0, 4, 0x00000001 }, + { 0x40, 0, 0, 0x0000001a }, + { jeq_src, 0, 15, LSB_src }, + { 0x48, 0, 0, 0x00000018 }, + { jeq_src, 12, 13, MSB_src }, + { 0x40, 0, 0, 0x00000012 }, + { jeq_src, 0, 11, LSB_src }, + { 0x48, 0, 0, 0x00000010 }, + { jeq_src, 8, 9, MSB_src }, + { 0x40, 0, 0, 0x00000006 }, + { jeq_dst, 0, 7, LSB_dst }, + { 0x48, 0, 0, 0x00000004 }, + { jeq_dst, 0, 5, MSB_dst }, + { 0x40, 0, 0, 0x0000000c }, + { jeq_src, 0, 3, LSB_src }, + { 0x48, 0, 0, 0x0000000a }, + { jeq_src, 0, 1, MSB_src }, + { 0x6, 0, 0, 0x00040000 }, + { 0x6, 0, 0, 0x00000000 } + }; + + now_cfg->filter.filter = (struct sock_filter*) malloc(sizeof(struct sock_filter)*now_cfg->filter.len); + memcpy(now_cfg->filter.filter, temp_code, sizeof(struct sock_filter) * now_cfg->filter.len); + + int sockopt_err = setsockopt(now_cfg->fd_if_socket, SOL_SOCKET, SO_ATTACH_FILTER, &now_cfg->filter, sizeof(now_cfg->filter)); + if(sockopt_err < 0) { + perror("Failed to set BPF socket options."); + + return ERR_FILTER_FAILED; + } + + struct IEEE80211_radiotap radiotap = IFNOW_RADIOTAP_DEFAULT(); + struct IEEE80211_wlan wlan = IFNOW_WLAN_DEFAULT(); + struct IEEE80211_actionframe actionframe = IFNOW_ACTIONFRAME_DEFAULT(); + struct IEEE80211_vendorspecific content = IFNOW_VENDORSPECIFIC_DEFAULT(); + + memcpy(&packet.radiotap, &radiotap, sizeof(struct IEEE80211_radiotap)); + memcpy(&packet.wlan, &wlan, sizeof(struct IEEE80211_wlan)); + memcpy(&packet.wlan.actionframe, &actionframe, sizeof(struct IEEE80211_actionframe)); + memcpy(&packet.wlan.actionframe.content, &content, sizeof(struct IEEE80211_vendorspecific)); + + printf("radiotap.channel_freq: %d\n", packet.radiotap.channel_freq); + + + pthread_create(&now_cfg->thread_id, NULL, &ifnow_thread_handle, now_cfg); +} diff --git a/src/tun.c b/src/tun.c new file mode 100644 index 0000000..53ac84f --- /dev/null +++ b/src/tun.c @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ifnow_tun.h" + +// this is called from the ESP Now receive callback +// it passes the contents received from the wifi interface +int ifnow_tun_send(ifnow_tun_cfg_t *tun_cfg, uint8_t *buf, uint8_t len) { + printf("tun: sending %d bytes to fd: %d\n", len, tun_cfg->fd_tuntap); + + for(uint16_t i = 0; i < len; i++) { + printf("%02X ", buf[i]); + if(i % 16 == 15) printf("\n"); + } + printf("\n"); + + return write(tun_cfg->fd_tuntap, buf, len); +} + +void *ifnow_tun_thread_handle(void *arg) { + ifnow_tun_cfg_t *tun_cfg = arg; + + printf("starting ifnow_tun thread.\n"); + + ssize_t receive_count = 0; + uint8_t receive_buffer[IFNOW_MAX_RECEIVE]; + + while(1) { + receive_count = read(tun_cfg->fd_tuntap, &receive_buffer, IFNOW_MAX_RECEIVE); + + if(receive_count > 0 && tun_cfg->receive != NULL) { + tun_cfg->receive(tun_cfg, (uint8_t*)&receive_buffer, receive_count); + } + }; +} + +int ifnow_tun_netlink_connect(ifnow_tun_cfg_t *tun_cfg) { + struct sockaddr_nl netlink_socket; + + tun_cfg->fd_netlink = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE); + if(tun_cfg->fd_netlink < 0) { + perror("Failed to get netlink socket"); + + return -4; + } + + memset(&netlink_socket, 0, sizeof(struct sockaddr_nl)); + netlink_socket.nl_family = AF_NETLINK; + int err = bind(tun_cfg->fd_netlink, (struct sockaddr*) &netlink_socket, sizeof(struct sockaddr_nl)); + if(err < 0) { + perror("Failed to bind netlink socket"); + + return -5; + } +} + +int ifnow_tun_netlink_set_ip(ifnow_tun_cfg_t *tun_cfg) { + struct { + struct nlmsghdr header; + struct ifaddrmsg content; + char attributes_buf[64]; + } request; + + struct rtattr *request_attr; + size_t attributes_buf_avail = sizeof request.attributes_buf; + + memset(&request, 0, sizeof request); + request.header.nlmsg_type = RTM_NEWADDR; + request.header.nlmsg_len = NLMSG_LENGTH(sizeof request.content); + request.header.nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; + request.content.ifa_index = if_nametoindex(tun_cfg->if_name); + request.content.ifa_family = AF_INET; + request.content.ifa_prefixlen = 32; + + /* request.attributes[IFA_LOCAL] = address */ + request_attr = IFA_RTA(&request.content); + + request_attr->rta_type = IFA_LOCAL; + request_attr->rta_len = RTA_LENGTH(sizeof (struct in_addr)); + request.header.nlmsg_len += request_attr->rta_len; + inet_pton(AF_INET, tun_cfg->addr_s, RTA_DATA(request_attr)); + + /* request.attributes[IFA_ADDRESS] = address */ + request_attr = RTA_NEXT(request_attr, attributes_buf_avail); + + request_attr->rta_type = IFA_ADDRESS; + request_attr->rta_len = RTA_LENGTH(sizeof (struct in_addr)); + request.header.nlmsg_len += request_attr->rta_len; + inet_pton(AF_INET, tun_cfg->addr_s, RTA_DATA(request_attr)); + + if (send(tun_cfg->fd_netlink, &request, request.header.nlmsg_len, 0) == -1) { + perror("Failed to connect netlink"); + + return -6; + } +} + +int ifnow_tun_netlink_up(ifnow_tun_cfg_t *tun_cfg) { + struct { + struct nlmsghdr header; + struct ifinfomsg content; + } request; + + printf("ifnow_tun_netlink_up: %s\n", tun_cfg->if_name); + + memset(&request, 0, sizeof request); + request.header.nlmsg_len = NLMSG_LENGTH(sizeof request.content); + request.header.nlmsg_flags = NLM_F_REQUEST; + request.header.nlmsg_type = RTM_NEWLINK; + request.content.ifi_index = if_nametoindex(tun_cfg->if_name); + request.content.ifi_flags = IFF_UP; + request.content.ifi_change = 1; + + if (send(tun_cfg->fd_netlink, &request, request.header.nlmsg_len, 0) == -1) { + perror("Failed to send netlink up"); + + return -1; + } + return 0; +} + +int ifnow_tun_tuntap_connect(ifnow_tun_cfg_t *tun_cfg) { + struct ifreq ifr; + + tun_cfg->fd_tuntap = open("/dev/net/tun", O_RDWR | O_CLOEXEC); + if(tun_cfg->fd_tuntap < 0) { + perror("Failed to open /dev/net/tun"); + + return -2; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + + if(tun_cfg->if_name != NULL) { + memcpy(ifr.ifr_name, tun_cfg->if_name, strlen(tun_cfg->if_name) + 1); + } + + int err = ioctl(tun_cfg->fd_tuntap, TUNSETIFF, &ifr); + if(err < 0) { + perror("Failed to set interface name"); + return -3; + } + +} + +int ifnow_tun_init(ifnow_tun_cfg_t *tun_cfg) { + printf("tun_cfg->addr_s: %s\n", tun_cfg->addr_s); + + int err = inet_pton(AF_INET, tun_cfg->addr_s, &tun_cfg->addr); + if(err < 0) { + perror("Failed to parse IP address"); + + return -1; + } + + ifnow_tun_tuntap_connect(tun_cfg); + ifnow_tun_netlink_connect(tun_cfg); + ifnow_tun_netlink_set_ip(tun_cfg); + // TODO send netlink route request + ifnow_tun_netlink_up(tun_cfg); + + pthread_create(&tun_cfg->thread_id, NULL, &ifnow_tun_thread_handle, tun_cfg); + + return 0; +}