libnet/udp_port_chain.c

libnet也支援輸入有範圍的或特定的Port。

這個程式可以對目標送出連續Port的UDP封包。

Source Code

//
//  udp_port_chain.c
//  功能:使用Port chain功能連續送出封包。
//  Created by 聲華 陳 on 2016/02/29.
//

#include <stdio.h>
#include <stdlib.h>
#include <libnet.h>

void usage(const char *cmd);
u_int32_t ip_aton(const char *ip_address);

int main(int argc, const char * argv[]) {
    int c;
    int interval = 100 * 1000; //0.1s
    libnet_t *handle = NULL;
    char errbuf[LIBNET_ERRBUF_SIZE];
    libnet_ptag_t ip_tag = LIBNET_PTAG_INITIALIZER, udp_tag = LIBNET_PTAG_INITIALIZER;
    u_int32_t dst_ip = 0;
    libnet_plist_t plist, *plist_p = NULL;
    u_int16_t bport, eport, cport;
    u_int16_t dport = 0;
    int length;
    struct libnet_stats ls;


    //init handle
    handle = libnet_init(LIBNET_RAW4, NULL, errbuf);
    if(!handle) {
        fprintf(stderr, "libnet_init: %s\n", errbuf);
        exit(1);
    }//end if

    //parse argument
    opterr = 0; //don't show default error message
    while((c = getopt(argc, (char * const *)argv, "w:d:p:P:")) != EOF) {
        switch (c) {
            case 'w':
                interval = atoi(optarg) * 1000;
                if(interval == 0) {
                    fprintf(stderr, "Invalid interval value: %s\n", optarg);
                    exit(1);
                }//end if
                break;

            case 'd':
                dst_ip = ip_aton(optarg);
                break;

            case 'P':
                plist_p = &plist;
                if (libnet_plist_chain_new(handle, &plist_p, optarg) == -1) {
                    fprintf(stderr, "libnet_plist_chain_new: %s\n", libnet_geterror(handle));
                    libnet_destroy(handle);
                    exit(1);
                }//end if
                break;

            case 'p':
                dport = atoi(optarg);
                if(dport == 0) {
                    fprintf(stderr, "Invalid port number: %s\n", optarg);
                    exit(1);
                }//end if
                break;

            case '?':
            case 'h':
            default:
                usage(argv[0]);
                break;
        }//end switch
    }//end while

    if(dst_ip == 0 || dport == 0 || !plist_p) {
        libnet_destroy(handle);
        usage(argv[0]);
    }//end if


    //get start port and end port
    while(libnet_plist_chain_next_pair(plist_p, &bport, &eport)) {
        //loop increase port
        while(bport <= eport && bport != 0) {
            cport = bport++;

            udp_tag = libnet_build_udp(cport,
                                       //source port
                                       dport,
                                       //destination port
                                       LIBNET_UDP_H,
                                       //length
                                       0,
                                       //checksum
                                       NULL,
                                       0,
                                       handle,
                                       udp_tag);
            if(udp_tag == -1) {
                fprintf(stderr, "libnet_build_udp: %s\n", libnet_geterror(handle));
                libnet_destroy(handle);
                exit(1);
            }//end if

            if(ip_tag == LIBNET_PTAG_INITIALIZER) {
                ip_tag = libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_UDP_H,
                                               IPPROTO_UDP,
                                               dst_ip,
                                               handle);

                if(ip_tag == -1) {
                    fprintf(stderr, "libnet_autobuild_ipv4: %s\n", libnet_geterror(handle));
                    libnet_destroy(handle);
                    exit(1);
                }//end if
            }//end if

            length = libnet_write(handle);
            if(length == -1) {
                fprintf(stderr, "libnet_write: %s\n", libnet_geterror(handle));
            }//end if
        }//end while
    }//end while

    //get state
    libnet_stats(handle, &ls);
    printf("Packets sent:  %lld\n"
            "Packet errors: %lld\n"
            "Bytes written: %lld\n",
            ls.packets_sent, ls.packet_errors, ls.bytes_written);

    //free
    libnet_plist_chain_free(plist_p);
    libnet_destroy(handle);

    return 0;
}

void usage(const char *cmd) {
    printf("Usage: %s <-d Destination IP Address> <-P Source Port-Port Range, or Source Port1,Port2,Port3...> <-p Destination Port> [-w Interval ms]\n", cmd);
    exit(1);
}//end usage

u_int32_t ip_aton(const char *ip_address) {
    u_int32_t ip_integer;
    if(1 != inet_pton(AF_INET, ip_address, &ip_integer)) {
        fprintf(stderr, "Invalid IP address: %s\n", ip_address);
        exit(1);
    }//end if
    return ip_integer;
}//end if

結果

libnet % sudo ./udp_port_chain
Usage: ./udp_port_chain <-d Destination IP Address> <-P Source Port-Port Range, or Source Port1,Port2,Port3...> <-p Destination Port> [-w Interval ms]
libnet % sudo ./udp_port_chain -d 192.168.1.1 -P 20-30 -p 8888
Packets sent:  11
Packet errors: 0
Bytes written: 308
libnet %

參數

-d #目的IP Address
-P #來源Port Number,格式Port-Port或Port1,Port2,Port3...
-p #目的Port Number
-w #每個封包間隔,單位毫秒,預設100毫秒

分析

void usage(const char *cmd);
u_int32_t ip_aton(const char *ip_address);

函數宣告有:函數usage()輸出該程式用法;函數ip_aton()將字串的IP Address轉成網路順序地址。


void usage(const char *cmd) {
    printf("Usage: %s <-d Destination IP Address> <-P Source Port-Port Range, or Source Port1,Port2,Port3...> <-p Destination Port> [-w Interval ms]\n", cmd);
    exit(1);
}//end usage

函數usage()可以看到Source Port輸入方式可以為:Port-Port或者是Port1,Port2,Port3...,兩種格式。


    //init handle
    handle = libnet_init(LIBNET_RAW4, NULL, errbuf);
    if(!handle) {
        fprintf(stderr, "libnet_init: %s\n", errbuf);
        exit(1);
    }//end if

因為在讀入參數的時候就需要一個libnet handle了,所以在解析參數前就先產生一個libnet handle。


    //parse argument
    opterr = 0; //don't show default error message
    while((c = getopt(argc, (char * const *)argv, "w:d:p:P:")) != EOF) {
        switch (c) {
            case 'w':
                interval = atoi(optarg) * 1000;
                if(interval == 0) {
                    fprintf(stderr, "Invalid interval value: %s\n", optarg);
                    exit(1);
                }//end if
                break;

            case 'd':
                dst_ip = ip_aton(optarg);
                break;

            case 'P':
                plist_p = &plist;
                if (libnet_plist_chain_new(handle, &plist_p, optarg) == -1) {
                    fprintf(stderr, "libnet_plist_chain_new: %s\n", libnet_geterror(handle));
                    libnet_destroy(handle);
                    exit(1);
                }//end if
                break;

            case 'p':
                dport = atoi(optarg);
                if(dport == 0) {
                    fprintf(stderr, "Invalid port number: %s\n", optarg);
                    exit(1);
                }//end if
                break;

            case '?':
            case 'h':
            default:
                usage(argv[0]);
                break;
        }//end switch
    }//end while

    if(dst_ip == 0 || dport == 0 || !plist_p) {
        libnet_destroy(handle);
        usage(argv[0]);
    }//end if

接著就開始解析輸入的參數,這邊大多數沒什麼特別的,只有讀入Source Port不同。

...略
            case 'P':
                plist_p = &plist;
                if (libnet_plist_chain_new(handle, &plist_p, optarg) == -1) {
                    fprintf(stderr, "libnet_plist_chain_new: %s\n", libnet_geterror(handle));
                    libnet_destroy(handle);
                    exit(1);
                }//end if
                break;
...略

我們需要一個libnet_plist_t的指標指向變數plist,之後傳參數都需要這個指標。函數libnet_plist_chain_new()用來產生一個Port chain,第三個參數optarg就是我們傳入的參數,不是Port-Port格式就是Port1,Port2,Port3...格式。


...略
    if(dst_ip == 0 || dport == 0 || !plist_p) {
        libnet_destroy(handle);
        usage(argv[0]);
    }//end if

一樣當沒有讀到這三個參數就顯示使用方法並結束程式。


    //get start port and end port
    while(libnet_plist_chain_next_pair(plist_p, &bport, &eport)) {
        //loop increase port
        while(bport <= eport && bport != 0) {
            cport = bport++;

            udp_tag = libnet_build_udp(cport,
                                       //source port
                                       dport,
                                       //destination port
                                       LIBNET_UDP_H,
                                       //length
                                       0,
                                       //checksum
                                       NULL,
                                       0,
                                       handle,
                                       udp_tag);
            if(udp_tag == -1) {
                fprintf(stderr, "libnet_build_udp: %s\n", libnet_geterror(handle));
                libnet_destroy(handle);
                exit(1);
            }//end if

            if(ip_tag == LIBNET_PTAG_INITIALIZER) {
                ip_tag = libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_UDP_H,
                                               IPPROTO_UDP,
                                               dst_ip,
                                               handle);

                if(ip_tag == -1) {
                    fprintf(stderr, "libnet_autobuild_ipv4: %s\n", libnet_geterror(handle));
                    libnet_destroy(handle);
                    exit(1);
                }//end if
            }//end if

            length = libnet_write(handle);
            if(length == -1) {
                fprintf(stderr, "libnet_write: %s\n", libnet_geterror(handle));
            }//end if
        }//end while
    }//end while

接著就是主要的讀出傳入的Port並建構封包送出。

    //get start port and end port
    while(libnet_plist_chain_next_pair(plist_p, &bport, &eport)) {
        //loop increase port
        while(bport <= eport && bport != 0) {
            cport = bport++;
...略

變數bport表示開始Port;變數eport表示結束Port;變數cport表示目前Port,函數libnet_plist_chain_next_pair()用來在plist_p指向的Port列表中下一對Port。

第一個while用來一直取得下一對Port,第二個while用來將取得的Port遞增,會這樣寫是因為Port列表設計關係。

如果是Port-Port格式的話,假如參數給-P 20-30,第一個while只會執行一次,取得的變數bport和變數eport分別為2030

如果是Port1,Port2,Port3...格式的話,假如參數給-P 40,76,21,第一個while會執行三次,變數bport和變數eport在這三次分別為,40407676以及2121

...略
            udp_tag = libnet_build_udp(cport,
                                       //source port
                                       dport,
                                       //destination port
                                       LIBNET_UDP_H,
                                       //length
                                       0,
                                       //checksum
                                       NULL,
                                       0,
                                       handle,
                                       udp_tag);
            if(udp_tag == -1) {
                fprintf(stderr, "libnet_build_udp: %s\n", libnet_geterror(handle));
                libnet_destroy(handle);
                exit(1);
            }//end if
...略

接著部分就是建構封包表頭了,一樣順序都是由上往下,函數libnet_build_udp()負責建構UDP封包表頭,第四個參數Checksum填入0表示自動算。

...略
            if(ip_tag == LIBNET_PTAG_INITIALIZER) {
                ip_tag = libnet_autobuild_ipv4(LIBNET_IPV4_H + LIBNET_UDP_H,
                                               IPPROTO_UDP,
                                               dst_ip,
                                               handle);

                if(ip_tag == -1) {
                    fprintf(stderr, "libnet_autobuild_ipv4: %s\n", libnet_geterror(handle));
                    libnet_destroy(handle);
                    exit(1);
                }//end if
            }//end if
...略

IP表頭部分一樣用tag標記只建構一次就好了。

...略
            length = libnet_write(handle);
            if(length == -1) {
                fprintf(stderr, "libnet_write: %s\n", libnet_geterror(handle));
            }//end if
        }//end while
    }//end while

建構完封包後就直接送出吧。


    //get state
    libnet_stats(handle, &ls);
    printf("Packets sent:  %lld\n"
            "Packet errors: %lld\n"
            "Bytes written: %lld\n",
            ls.packets_sent, ls.packet_errors, ls.bytes_written);

封包建構完後在釋放資源之前,可以用函數libnet_stats()來取得封包送出狀況。


    //free
    libnet_plist_chain_free(plist_p);
    libnet_destroy(handle);

最後一樣釋放資源。

結語

libnet所提供的Port chain功能雖然很少會直接拿來使用,因為Port會是連續的,我們可以再另外改寫一下就可以把Port給打散了。

results matching ""

    No results matching ""