به نام خدا.

سلام داستان از اونجا شروع شد که برای انجام یکی از تسک ها نیاز بود از دو دستور زیر استفاده کنم:

iptables -t mangle -A OUTPUT -p tcp --dport 443 -m mark --mark 1 -j ACCEPT
iptables -t mangle -A OUTPUT -p tcp --dport 443 -j NFQUEUE --queue-num 0

این دستوارت مربوط به iptables هستند و دستور اول بیان میکنه که هر بسته ای به پورت مقصد 443 که با عدد ۱ مارک شده بود رو بپذیر در حالیکه دستور زیر به iptable میگه همه ی بسته هایی که به مقصد 443 میرن رو به NFQUEUE بفرست. NFQUEUE چی هست؟ عملا یک ویژگی توی کرنل لینوکس اضافه شده که میشه به کمک اون یک سری بسته ها رو درون یک صفی قرار داد که برنامه های یوزر اسپیس بتونن اونها رو پردازش کنن و اونها رو تغییر بدن یا جلوی بعضی از اونها رو بگیرند یا کلا ارسال اونها رو ادامه بدن و...

 

منم هدفم همین بود که همه ی بسته های client hello رو در بیارم و دستکاری کنم پس از این استفاده کردم.

همچنین برای استفاده از این قابلیت توی زبان سی باید از یک لایبراری به اسم libnetfilter_queue استفاده کرد و هدر هاش رو لود کرد و کتابخونه اش رو به برنامه پیوند زد.

 

متاسفانه اون دو تا دستور اول مربوط به iptable توی دستگاه خطا میداد و عملا ساپورت نمیشد. خود iptable توی دستگاه بود اما ویژگی match mark و ویژگی NFQUEUE رو نداشتن.

بعد از تحقیق متوجه شدم این ها همه به صورت کرنل ماژول هستن و حتی توی سیستم خوم هم که کار میکنه با lsmod میشه اونها رو دید.

پس به این نتیجه رسیدم که اگه سورس image مربوط به دستگاه رو بگیرم و کامپایل کنم احتمالا بعدش میتونم توی کانفیگ لینوکس اش اون چیزایی که میخواهیم رو اضافه کنم. اما اینکار کمی طولانی به نظر میرسد و احساس کردم برای اجرای این دو تا دستور ارزشش رو نداره انقدر بخواهم دیپ بشم.

برای همین رفتم سراغ TUN و TAP در لینوکس عملا TUN توی لینوکس یه اینترفیس مجازی ایجاد میکنه و باعث میشه بسته ها به سمت این اینترفیس هدایت بشن.

 

 9601  sudo ip tuntap add dev tun0 mode tun user $USER
 9604  sudo ip addr add 10.0.0.1/24 dev tun0
 9607  ip link set up tun0
 9693  sudo ip route change default dev tun0

 

به کمک دستورات بالا میشه یک دیوایس tun اضافه کرد بهش آیپی داد و اون رو به عنوان اینترفیس دیفالت به لینوکس معرفی کرد.

برای کار بار tun توی زبان سی باید فایل /dev/tun رو باز کرد و از اونجا باهاش کار کرد.

بعد از اینکه tun رو راه انداختم چیزای جالبی فهیمدم از جمله اینکه بسته هایی که توی این مرحله به ما میرسه عملا هدر لایه ی دوم یعنی MAC رو نداره. پس خودم دستی رفتم مک رو دراوردم و به بتدای بسته اضافه کردم مک خیلی آسون کلا دو تا ۶ بایت برای مک مبدا و مقصد هست که یکیش مک اینترفیس هست یکیش مک مودم که باید به دست بیاریم و ۲ بایت هم برای اینکه اعلام کنه نوع payload چیه که مثلا میتونه TCP باشه. یه نکته ای هم بگم برای من بسته هایی که میومد یه ۴ بایت اولش بود که معنی نمیداد من اونو اسکیپ میکردم.

اینم کدش میشه:

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>

#include <linux/if.h>
#include <linux/if_tun.h>
#include <sys/socket.h>
#include <sys/types.h>

#include <net/if_arp.h>
#include <netinet/ip.h>
#define BUF_SIZE 1500
#define ETH_HDR_LEN 14

// Function to open the TUN device
int open_tun_device(char* devname)
{
    struct ifreq ifr;
    int fd, err;

    if ((fd = open("/dev/net/tun", O_RDWR)) == -1) {
        perror("open /dev/net/tun");
        exit(1);
    }
    memset(&ifr, 0, sizeof(ifr));
    ifr.ifr_flags = IFF_TUN;
    strncpy(ifr.ifr_name, devname, IFNAMSIZ); // devname = "tun0" or "tun1", etc

    /* ioctl will use ifr.if_name as the name of TUN
     * interface to open: "tun0", etc. */
    if ((err = ioctl(fd, TUNSETIFF, (void*)&ifr)) == -1) {
        perror("ioctl TUNSETIFF");
        close(fd);
        exit(1);
    }

    /* After the ioctl call the fd is "connected" to tun device specified
     * by devname ("tun0", "tun1", etc)*/

    return fd;
}
// Function to open a socket for the physical interface
int open_physical_interface(const char *dev, struct ifreq *ifr) {
    int sockfd;

    // Open a socket
    if ((sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) {
        perror("Socket");
        return -1;
    }

    // Clear the ifreq structure
    memset(ifr, 0, sizeof(struct ifreq));

    // Set the device name
    strncpy(ifr->ifr_name, dev, IFNAMSIZ);

    // Get the interface index
    if (ioctl(sockfd, SIOCGIFINDEX, ifr) < 0) {
        perror("ioctl(SIOCGIFINDEX)");
        close(sockfd);
        return -1;
    }

    return sockfd;
}

// Function to get the MAC address of the physical interface
int get_mac_address(const char *dev, unsigned char *mac) {
    struct ifreq ifr;
    int sockfd;

    // Open a socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("Socket");
        return -1;
    }

    // Clear the ifreq structure
    memset(&ifr, 0, sizeof(struct ifreq));

    // Set the device name
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);

    // Get the MAC address
    if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) {
        perror("ioctl(SIOCGIFHWADDR)");
        close(sockfd);
        return -1;
    }

    memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
    close(sockfd);
    return 0;
}

// Function to perform an ARP request
int arp_request(const char *dev, const char *ip_addr, unsigned char *mac) {
    struct arpreq arp;
    struct sockaddr_in *sin;
    int sockfd;

    // Open a socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("Socket");
        return -1;
    }

    // Prepare ARP request
    memset(&arp, 0, sizeof(arp));
    arp.arp_ha.sa_family = ARPHRD_ETHER;
    // arp.arp_ha.sa_len = 6;
    arp.arp_pa.sa_family = AF_INET;
    sin = (struct sockaddr_in *)&arp.arp_pa;
    sin->sin_addr.s_addr = inet_addr(ip_addr);

    // Set interface name
    strncpy(arp.arp_dev, dev, sizeof(arp.arp_dev));

    // Perform ARP request
    if (ioctl(sockfd, SIOCGARP, &arp) < 0) {
        perror("ioctl(SIOCGARP)");
        close(sockfd);
        return -1;
    }

    memcpy(mac, arp.arp_ha.sa_data, 6);
    close(sockfd);
    return 0;
}

// Function to construct an Ethernet frame
void construct_ethernet_frame(unsigned char *frame, unsigned char *src_mac, unsigned char *dst_mac, unsigned short eth_type, unsigned char *payload, int payload_len) {
    struct ethhdr *eth = (struct ethhdr *)frame;

    memcpy(eth->h_source, src_mac, 6);
    memcpy(eth->h_dest, dst_mac, 6);
    eth->h_proto = htons(eth_type);

    memcpy(frame + ETH_HDR_LEN, payload, payload_len);
}

// Function to get the IP address of an interface
int get_ip_address(const char *dev, char *ip_addr) {
    struct ifreq ifr;
    int sockfd;

    // Open a socket
    if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
        perror("Socket");
        return -1;
    }

    // Clear the ifreq structure
    memset(&ifr, 0, sizeof(struct ifreq));

    // Set the device name
    strncpy(ifr.ifr_name, dev, IFNAMSIZ);

    // Get the IP address
    if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) {
        perror("ioctl(SIOCGIFADDR)");
        close(sockfd);
        return -1;
    }

    // Convert IP address to string
    strcpy(ip_addr, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr));
    close(sockfd);
    return 0;
}


// Function to calculate IP checksum
unsigned short calculate_checksum(unsigned short *buf, int nwords) {
    unsigned long sum;
    for (sum = 0; nwords > 0; nwords--) {
        sum += *buf++;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    return (unsigned short)(~sum);
}

// Function to modify the source IP address in the IP header
void modify_ip_source_address(unsigned char *packet, const char *new_src_ip) {
    struct ip *iph = (struct ip *)packet;
    size_t ip_header_len = iph->ip_hl * 4;

    // Convert new source IP address to network byte order
    inet_pton(AF_INET, new_src_ip, &(iph->ip_src));

    iph->ip_sum = 0;
    iph->ip_sum = calculate_checksum((unsigned short *)iph, ip_header_len / 2);
}




int main() {
    char tun_device[] = "tun0";  // Path to the TUN device
    char phy_name[] = "wlo1";
    int tun_fd, phy_fd;
    struct ifreq ifr;
    unsigned char buffer[BUF_SIZE];
    unsigned char frame[BUF_SIZE + ETH_HDR_LEN];
    unsigned char src_mac[6], dst_mac[6];
    int nread;
    char src_ip[INET_ADDRSTRLEN];

    // Open the TUN device
    tun_fd = open_tun_device(tun_device);
    if (tun_fd < 0) {
        fprintf(stderr, "Error opening TUN device\n");
        return 1;
    }

    // Open the physical interface
    phy_fd = open_physical_interface(phy_name, &ifr);
    if (phy_fd < 0) {
        fprintf(stderr, "Error opening physical interface\n");
        return 1;
    }

    // Get the MAC address of the physical interface
    if (get_mac_address(phy_name, src_mac) < 0) {
        fprintf(stderr, "Error getting MAC address of physical interface\n");
        return 1;
    }
    
    // Set destination MAC address 
    arp_request(phy_name, "192.168.1.1", dst_mac);

    // Get the IP address of the physical interface
    if (get_ip_address(phy_name, src_ip) < 0) {
        fprintf(stderr, "Error getting IP address of physical interface\n");
        return 1;
    }


    printf("Reading from TUN device %s and forwarding to %s\n", tun_device, phy_name);

    // Main loop to read data from TUN device and forward to physical interface
    while (1) {
        // Read a packet from the TUN device
        nread = read(tun_fd, buffer, sizeof(buffer));
        if (nread < 0) {
            perror("Reading from TUN device");
            close(tun_fd);
            close(phy_fd);
            return 1;
        }

        // Print the packet (in hexadecimal format)
        printf("Read %d bytes from %s: ", nread, tun_device);
        for (int i = 0; i < nread; i++) {
            printf("%02x ", (unsigned char)buffer[i]);
        }
        printf("\n");

        modify_ip_source_address(&buffer[4], src_ip);

        // Construct the Ethernet frame
        construct_ethernet_frame(frame, src_mac, dst_mac, ETH_P_IP, &buffer[4], nread - 4);

        // Prepare sockaddr_ll structure for sending the packet
        struct sockaddr_ll sa;
        memset(&sa, 0, sizeof(sa));
        sa.sll_family = AF_PACKET;
        sa.sll_ifindex = ifr.ifr_ifindex;
        sa.sll_protocol = htons(ETH_P_IP);

        // Forward the packet to the physical interface
        if (sendto(phy_fd, frame, nread + ETH_HDR_LEN - 4, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
            perror("Sending to physical interface");
            close(tun_fd);
            close(phy_fd);
            return 1;
        }
    }

    close(tun_fd);
    close(phy_fd);
    return 0;
}

من میخوستم با این کد هرچی میگیرم رو بدم توی خروجی و عملا یه فورواردر خالی باشم اما نشد چون مجبور میشدم آیپی مبدا رو عوض کردم چون توی بسته ای که به من میرسد آیپی مبدا با مقدار آیپیه اینترفیس پر شده بود که فکر میکنم کار کرنل هست پس من به جای آیپی اینترفیس خودم آیپی اینترفیس اصلی فیزیکی رو میدادم (که مثلا ۱۹۲.۱۶۸.۱.۷ بود) ولی چون آیپی عوض شده بود عملا موقعی که درخواست داده میشد اوکی بود ولی وقتی سرور جواب میداد لینوکس میگفت این بسته با این آیپی و پورت مال ما نیست و عملا DROP اش میکرد و چیزی به برنامه نمیرسد.

 

در هر صورت من با این روش نتونستم یه Forwarder ساده بسازم پس بی خیال اش شدم و قیدش رو با این روش زدم و گذاشتم برای روزی که بیشتر از طرز کارش سر در بیارم ولی چیزای خوبی یاد گرفتم.

 

دوباره برگشتم سمت اینکه برم مثل آدم همون کرنل ماژول های مربوط به iptables رو کامپایل کنم پس اول رفتم سورس دستگاه رو گرفتم و شروع کردم به کامپایل کردم از اونجایی که من از آرچ لینوکس استفاده میکنم و توی آرچ همه بسته ها روی نسخه های آخر خودشون هستن برای همین خیلی از برنامه ها توی Compatibility به مشکل میخورن مثلا تا الان فلان چیز توی GCC اوکی بوده اما توی آپدیت آخرش دیگه خطا میده که این روش برای اعلان تابع امکان پذیر نیست. حالا ما مجبوریم کد رو اصلاح کنیم که دردسر های خودش رو داره.

برای شروع به من چند تا خطا از این دست داد که من سعی کردم رفع شون کنم اما دیدم از ۳۰۰ تا بسته ای که میخواد دانلود و کامپایل کنه تازه ما بسته ی ۳۰ ام هستیم بعد از یک روز. برای همین گفتم این روش به این صورت فایده نداره. بهترین راه در این مواقع استفاده از ابزار هایی هست که خودشون این image رو باهاش کامپایل کردن با توجه به گستردگی اوبونتو معمولا این ها روی اوبونتو کامپایل میشه برای همین منم چون دوست نداشتم اوبونتو نصب کنم رفتم سراغ داکر و با سه تا دستور زیر یک اوبونتو ایجاد کردم.

sudo pacman -S docker
docker pull ubuntu:20.04
docker run -it ubuntu:20.04
docker start -it awesome_goldwasser
docker exec -it awesome_goldwasser /bin/bash

البته اگه اولش سرویس داکر ران نباشه خطای سوکت میده برای ران کردنش هم میتونید از systemctl start docker استفاده کنید.

توی اوبونتو که نصب شد شروع کردم ابزار های کامپایل و binutil رو دانلود کردن همچنین git رو.

بعد سورس رو به اونجا انتقال دادم و شروع کردم به کامپایل سورس توی اونجا انگار همه چیز بدون مشکل داشت پیش میرفت اما یه چند بار بخاطر خطای disk space متوقف شد که مجبور شدم کل پارتیشن رو براش خالی کنم همچنین جای دیتا ی داکر رو عوض کردم و از /var/lib/docker به یه پارتیشن دیگه انتقال دادم و تنها کافی برای توی کانفیگ داکر بهش جای جدید رو معرفی کنیم.

 

sudo nano /etc/docker/daemon.json

مقدار زیر رو قرار بدید:

{
  "data-root": "/mnt/program/docker-data"
}

و قبلش هم سرویس داکر رو متوقف کنید بعدش دوباره ران کنید با دستور docker info هم میتونید ببینید تغییراتتون توسط داکر شناسایی شده یا نه اگه شناسایی نشده بود دوباره سرویس اش رو یک ری استارت بکنید.

 

با این حالت تقریبا بدون مشکل یا صرفا با یکی دو تا مشکل سر url ها که ۴۰۴ میدادن (و مجبور شدم لینکشون رو عوض کنم) مشکل حل شد و ایمیج ساخته شد) اینکار تقریبا ۲ روز زمان برد و تازه من به فایل image اولیه رسیدم و تازه باید میرفتم کرنل ماژول ها رو اوکی میکردم.

از اونجایی که دلم نمیخواست ندانسته کاری رو انجام بدم شروع کردم به خوندن make file هاش و اسکریپت هاش که ببینم چطوری و کجا من باید کرنل ماژول ها رو اضافه کنم. به صورت کلی طرز کار ساخت ایمیج اش کاملا دستی بود و از هیچ ابزاری استفاده نشده بود یعنی یه عالمه اسکریپت نوشته شده بود برای اینکه pacakge ها رو شناسایی کنه بره دانلود کنه بیاد بازشون کنه و کانفیگ کنه و کامپایل کنه همچنین برای patch هم ایده داده بودن که میشد برای هر package یک patch داشت همچنین برای device ها هم کار کرده بودن که مثلا بشه برای دیوایس های مختلف با معماری های مختلف کامپایل رو انجام داد.

من که وارد پوشه ی projects/Amlogic-ce/devices/Amlogic-ng/linux شدم متوجه شدم اونجا یه کانفیگ لینوکس قرار داره و حدس زدم طرز فکرشون این بوده که با توجه به هر دیوایسی یک کانفیگ براش در نظر گرفته شده که طبق اون درایور ها کامپایل میشه و با این طرز تفکر رفتم توی کد رو خوندم و دیدم بله توی اسکریپت ها هم داره چند جا رو برای فایل کانفیگ لینوکس میخونه که یکیش همینجاست و بعد متوجه شدم از همین فایل برای کانفیگ استفاده میکنه عملا این فایل رو با اسم .config میزاره کنار سورس لینوکس.

 

من این فایل رو تغییر دادم ولی نمیدونستم چطور میتونم بهش بگم کرنل لینوکس رو مجدد کامپایل کن. تا اینکه بعد متوجه شدم باید کرنل لینوکس رو از از مکان زیر پاک میکردم که دوباره تلاش کنه برای اکسترکت کردن و کامپایل کردنش:

rm ./build.CoreELEC-Amlogic-ng.arm-19/build/linux-4030a2825ee648c332c54c2857c3b63c809c9da2 -r

 

بعد از پاک کردن گاهی تشخیص میداد کامپایل میکرد گاهی کامپایل نمیکرد من دلیلش رو نمیدونم. یک جایی بود که من یک فایلی رو با فایل اکسپلورر لینوکس از توی اوبونتو حذف کردم و دیدم در کمال تعجب داره از فایل هنوز استفاده میکنه و خطا نداره. هرچی ls -la میزدم فایل وجود نداشت ولی وقتی cat میکردم اطلاعات درون فایل رو نشون میداد یا وقتی فایل رو cp میزدم واقعا فایل کپی میشد از اونجا به داکر شک کردم که شاید وقتی با ابزار های خارجی وارد فایل سیستم اش میشیم به مشکل میخوره و دیگه هر موقع اتفاق غیر منتظره ای میوفتاد حواسم رو جمع میکردم که فایلی که تغییر دادم حتما تغییر کرده باشه و...

 

بچه های توسعه دهنده چند تا اسکریپت کمکی نوشته بودن و توی پوشه tools قرار داده بودن مثلا اسکریپت های زیر:

./tools/viewconfig
./tools/viewplan
./tools/adjust_kernel_config
./tools/check_kernel_config

با viewconfig میتونید کانفیگ کل پروژه رو مشاهده کنید (مثل رمز و یوزرنیم روت در فایل ایمیج نهایی و...)

با دستور دوم که viewplan نام داره میتونید پلن رو ببینید چیزیکه من از پلن فهمیدم اینه که میاد package ها رو پارس میکنه و بر اساس dependency ها میچینه و این میشه plan برای کامپایل و توی پلن ما دو دسته کار داریم یکی بیلد هست و دیگری install که میگه مثلا بعد از بیلد اینها رو کجا بریز.

برای دستور سوم یعنی adjust_kernel_config هم که از اسمش مشخصه و به شما اجازه میده بدون دستکاری مستقیم و از اینجا بتونید کانفیگ های کرنل رو تغییر بدید و این همون چیزی هست که ما میخواهیم.

به کمک همین adjust_kernel_config میتونید ببینید آیا تغییراتی که توی کانفیگ دادید دیده شده یا نه. اگه اوکی بود میتونید یه بار دیگه دستور make رو بزنید که از اول پکیج ها رو بیلد کنه و میبینید که کرنل لینوکس رو از اول کامپایل میکنه و درایور هاش رو انتقال میده.

 

نکته من هرچی فایل package.mk مربوط به لینوکس رو تغییر دادم هیچ تاثیری نداشت و احتملا بخاطر باگ مربوط به داکر بود خیلی حواستون جمع باشه.

 

در نهایت کامپایل کرنل ماژول ها انجام شد و من اونها رو بردم داخل دستگاه و با insmod لود کردم و اوکی بود و لود شد و دستورات اولیه که در ابتدای مقاله نوشتم بدون مشکل کار میکرد.

 

بعد نوبت رسید به لایبراری که گفتم libnetfilter_queue.so که باید کامپایلش میکردیم برای دستگاه.

برای انجام اینکار دوباره رفتم سراغ استفاده از کامپایلری که برای ساخت این image ایجاد شده بود اما تلاش اولیه نتیجه نداد و خیلی خطاهای گوناگونی میداد.

 

رفتم آرچ لینوکس رو روی دستگاه بالا آوردم با کمک اسکریپتی که توی گیتهابم گذاشتم.

https://github.com/fallaha/arch-bootstrap

 

و یه عالمه زحمت کشیدم که آرچ رو بالا بیارم و pacman رو بالا آوردم و همه شرایط رو فراهم کردم و برنامه رو کامپایل کردم و با موفقیت همه چیز انجام شد و حتی برنامه رو تست کردم و کار میکرد.

 

اما این فقط وقتی کار میکرد که توی آرچ chroot کرده بودیم ولی وقتی با سیستم واقعی اش فایل رو اجرا میکردم خطای GLIBC میداد. برنامه دنبال GLIBC2.34 میگشت درحالیکه سیستم نسخه ی GLIBC2.32 رو ساپورت میکرد خیلی تلاش کردم که اینو با اون Compatible کنم اما نشد.

 

برای همین دوباره برگشتم سمت اینکه کراس کامپایلینگ انجام بدم.

بعد از اتفاق بالا این سوال برام پیش اومد که اگه موقع کامپایل برنامه نسخه ی GLIBC مهم هست پس توی کراس کامپایلینگ چه اتفاقی میوفته اینجا بود که مشکل کارم رو فهیمدم. اینکه چرا انقدر بار اول خطا میداد مشکلش این بود که من داشتم صرفا از کامپایلر استفاده میکردم در حالیکه برای کراس کامپایلینگ ما به یک TOOLChain نیاز داریم که شامل binutils, glibc , gcc میشه ولی من فقط داشتم با gcc کار میکردم. عملا متوجه شدم که باید توی تولچین که داریم حتما libc.so وجود داشته باشه وقتی سرچ کردم دیدم بله یه پوشه به اسم sysroot وجود داره که ساختار داخلی اش شبیه لینوکس هست و توی اونجا libc.so که همون glibc هست به علاوه ی util ها وجود داره.

بعد از اون سورس لایبراری libnetfilter_queue به همراه وابستگی هاش که libnfnetlink و libmnl بود رو دانلود کردم و با دستورات زیر اونها رو کامپایل کردم:

 

   71  export HU=/home/ali/CoreELEC-19.4-Matrix_rc2/build.CoreELEC-Amlogic-ng.arm-19/toolchain/bin/
   72  echo $HU
   73  export CC=$HU/armv8a-libreelec-linux-gnueabihf-gcc
   74  export CXX=$HU/armv8a-libreelec-linux-gnueabihf-g++
   75  export AR=$HU/armv8a-libreelec-linux-gnueabihf-ar
   76  export AS=$HU/armv8a-libreelec-linux-gnueabihf-as
   77  export LD=$HU/armv8a-libreelec-linux-gnueabihf-ld
   78  export RANLIB=$HU/armv8a-libreelec-linux-gnueabihf-ranlib

 

برای کامپایل libmnl و libnfnetlink دستور زیر :

  116  ./autogen.sh 
  117  ./configure --host=arm-linux-gnueabihf
  118  make

و برای libnetfilter_queue که به این دوتا نیاز داشت از دستور زیر:

LIBNFNETLINK_LIBS="-L/home/ali/libnfnetlink/src/.libs" LIBNFNETLINK_CFLAGS="-I/home/ali/libnfnetlink/include/" LIBMNL_LIBS="-L/home/ali/libmnl/src/.libs" LIBMNL_CFLAGS="-I/home/ali/libmnl/include" ./configure --host=arm-linux-gnueabihf
make

بعد از اینکه اینا کامپایل شد نیاز بود برنامه خودم رو کامپایل کنم که توش از این لایبراری استفاده میشد.

که با دستور زیر اینکار رو انجام دادم:

242  $CC ./frag_test.c -I./usr/include -L./lib -lnetfilter_queue -lmnl -l nfnetlink -o ./fragment_test

واینطوری بود که بالاخره تونستم برنامه رو با کراس کامپایلر برای دستگاه کامپایل کنم.

خیلی دوست دارم در مورد toolchain بیشتر اطلاعات بدست بیارم و اینکه چطور خودمون میتونیم تولچین خودمون روبسازیم و اینکه چطوری sysroot رو به کامپایلر معرفی میکنند که از اون لایبراری ها استفاده کنه و...