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
分別為20
和30
。
如果是Port1,Port2,Port3...
格式的話,假如參數給-P 40,76,21
,第一個while
會執行三次,變數bport
和變數eport
在這三次分別為,40
和40
、76
和76
以及21
和21
。
...略
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給打散了。