2013年3月27日 星期三

iptables reject and redirect to url

自己寫的,較舊版本的iptable沒有redirect url功能。

//Reference:
//http://gpl.back2roots.org/source/puma5/netgear/CG3300D_V1.00.15_src/apps/fw_modules/ipt_REJECT.c
//http://bbs.chinaunix.net/thread-1960367-1-1.html
//http://blog.chinaunix.net/uid-27087754-id-3279302.html
//http://blog.csdn.net/yadon_z/article/details/7983402
//http://code.google.com/p/wl500g/source/browse/branches/rt-n/kernel-2.6/606-netfilter-webstr.patch?r=2456
#define MAX_HTTP_REDIRECT_CONTENT 512
static void send_http_redirect(struct sk_buff *oldskb, int hook, const char *data){   char http_content[MAX_HTTP_REDIRECT_CONTENT], *pHttp_content;   sprintf(http_content, "HTTP/1.1 307 FoundrnServer: ZyXEL: Content Filter/1.03nLocation:%srn", data);
strcat(http_content, "Date: Thu, 17 Feb 2000 12:46:46 GMTrnContent-Type: text/htmlrnAccept-Ranges: bytesrnContent-Length: 256rn");
strcat(http_content, "Cache-control: no-cachernPragma: no-cachernExpires: 0rnConnection: closernrn");   struct sk_buff *nskb;
const struct iphdr *oiph;
struct iphdr *niph;
struct tcphdr *oth;
struct tcphdr _otcph, *tcph;
unsigned int addr_type;
unsigned int otcplen;   /* IP header checks: fragment. */
if (ip_hdr(oldskb)->frag_off & htons(IP_OFFSET))
return;   oth = skb_header_pointer(oldskb, ip_hdrlen(oldskb),
sizeof(_otcph), &_otcph);
otcplen = oldskb->len - ip_hdr(oldskb)->ihl*4;
if (oth == NULL)
return;   /* No RST for RST. */
if (oth->rst)
return;   /* Check checksum */
if (nf_ip_checksum(oldskb, hook, ip_hdrlen(oldskb), IPPROTO_TCP))
return;
oiph = ip_hdr(oldskb);     unsigned char *http_data = (void *)oth + oth->doff*4;
unsigned int datalen = (oldskb)->len - (oiph->ihl*4) - (oth->doff*4);   /*debug message*/
/*
char show_data[20];
memset(show_data, 0, sizeof(show_data));
memcpy(show_data, http_data, sizeof(show_data) - 1);
printk(KERN_EMERG "__[Kernel]datalen=%d http data=%sn", datalen,show_data);   if (datalen < 10){
return;
}
*/
   if(memcmp(http_data, "GET", sizeof("GET") - 1) != 0 &&
memcmp(http_data, "POST", sizeof("POST") - 1) != 0){
return;
}   nskb = alloc_skb(sizeof(struct iphdr) + sizeof(struct tcphdr) +
LL_MAX_HEADER + strlen(http_content), GFP_ATOMIC);
if (!nskb)
return;   skb_reserve(nskb, LL_MAX_HEADER);   skb_reset_network_header(nskb);
niph = (struct iphdr *)skb_put(nskb, sizeof(struct iphdr));
niph->version = 4;
niph->ihl = sizeof(struct iphdr) / 4;
niph->tos = 0;
niph->id = 0;
niph->frag_off = htons(IP_DF);
niph->protocol = IPPROTO_TCP;
niph->check = 0;
niph->saddr = oiph->daddr;
niph->daddr = oiph->saddr;   tcph = (struct tcphdr *)skb_put(nskb, sizeof(struct tcphdr));
memset(tcph, 0, sizeof(*tcph));
tcph->source = oth->dest;
tcph->dest = oth->source;
tcph->doff = sizeof(struct tcphdr) / 4;     pHttp_content = (char *)skb_put(nskb, strlen(http_content));
strcpy(pHttp_content, http_content);   tcph->seq = oth->ack_seq;
tcph->ack_seq = htonl(ntohl(oth->seq) + oth->syn + oth->fin
+ otcplen - (oth->doff<<2));   tcph->fin = 1;
tcph->psh = 1;
tcph->ack = 1;   tcph->rst = 0;   nskb->csum = csum_partial((char *)tcph + tcph->doff*4,
strlen(http_content), 0);   tcph->check = tcp_v4_check(sizeof(struct tcphdr) + strlen(http_content),
niph->saddr, niph->daddr,
csum_partial(tcph,
sizeof(struct tcphdr), nskb->csum));   addr_type = RTN_UNSPEC;
if (hook != NF_INET_FORWARD
#ifdef CONFIG_BRIDGE_NETFILTER
|| (nskb->nf_bridge && nskb->nf_bridge->mask & BRNF_BRIDGED)
#endif
)
addr_type = RTN_LOCAL;   /* ip_route_me_harder expects skb->dst to be set */
dst_hold(oldskb->dst);
nskb->dst = oldskb->dst;   if (ip_route_me_harder(nskb, addr_type))
goto free_nskb;   niph->ttl = dst_metric(nskb->dst, RTAX_HOPLIMIT);
nskb->ip_summed = CHECKSUM_NONE;   /* "Never happens" */
if (nskb->len > dst_mtu(nskb->dst))
goto free_nskb;   nf_ct_attach(nskb, oldskb);   ip_local_out(nskb);   return;   free_nskb:
kfree_skb(nskb);   } 

2012年10月24日 星期三

Passing string as -D compiler flag to .c

有時候有些字串需要compiler time時決定。通常前期先用menuconfig之類的工具設定好,再進行compile動作,此時新字串就會自動帶進.c中。

要做到這樣的效過,在gcc中可以用-D方式指定。

gcc編譯中加入 -DNEW_STRING=\"xxxxxxx\"。

在c原始碼內,利用一些字串連接的技巧串起來。或者可以自己加入#define中。

char a* = "I got "NEW_STRING"!!!!";

這樣目的就達成了。


參考資料
[1] Passing string as -D compiler option

2012年8月26日 星期日

Linux get ipv6 default route

在ipv6情況下可以從/proc/net/ipv6_route印出當前default route資訊。試了一下發現內容順序以及值會跟隨目前routing作改變。在參考了route_linux.c以及how to get ipv6 default gateway from proc in linux之後,改寫出以下範例。

int getIpv6DefaultGatway(char *ifname, char *dgw, int dgw_size){
struct in6_addr src_addr;
int src_prefix;
struct in6_addr dst_addr;
int dst_prefix;
struct in6_addr gw_addr;
struct in6_addr any_addr;
unsigned int metrix;
unsigned int ref_cnt;
unsigned int usr_cnt;
unsigned int flag;
char devname[32];
FILE *fp;
int ret;
char dst[64];
char src[64];
char gw[64];

if(ifname == NULL){
return 0;
}

strcpy(dgw, "");

fp = fopen("/proc/net/ipv6_route", "r");
if(fp!=NULL){
memset(&any_addr, 0, sizeof(any_addr));
while((ret=fscanf(fp, "%s %x %s %x %s %x %x %x %x %s",
dst, &dst_prefix, src, &src_prefix, gw,
&metrix, &ref_cnt, &usr_cnt, &flag, devname)) != EOF){


/*return value must be parameter's number*/
if(ret != 10){
continue;
}

/*default route?*/
if(!strncmp(gw, "fe80", 4)
&& !strcmp(dst, "00000000000000000000000000000000") && dst_prefix == 0
&& !strcmp(ifname, devname)){


sprintf(gw, "%c%c%c%c:%c%c%c%c:%c%c%c%c:%c%c%c%c:%c%c%c%c:%c%c%c%c:%c%c%c%c:%c%c%c%c",
gw[0], gw[1], gw[2], gw[3], gw[4], gw[5], gw[6], gw[7], gw[8], gw[9],
gw[10], gw[11], gw[12], gw[13], gw[14], gw[15], gw[16], gw[17], gw[18], gw[19],
gw[20], gw[21], gw[22], gw[23], gw[24], gw[25], gw[26], gw[27], gw[28], gw[29],
gw[30], gw[31]);

inet_pton(PF_INET6, gw, &gw_addr);
inet_ntop(PF_INET6, &gw_addr, gw, sizeof(gw));

snprintf(dgw, dgw_size, "%s", gw);

//DO NOT BREAK to get last gateway
}
}
}
fclose(fp);

if(!strcmp(dgw, "")){
return 0;
}

return 1;
}

2012年8月21日 星期二

exec, spawn, system不同點

根據stackoverflow中Difference b/w exec( ) and system( ) in unix這篇文章中討論到的

exec: 覆蓋原本的程式,執行完即結束。

spawn: 啟動一個新的子程式(fork效用相當),並且和原本的程式同時進行。

system: 啟動一個新的子程式(fork效用相當),並且原本程式會等待該子程式執行完後才結束。

2012年7月25日 星期三

Find process and send signal to it in Kernel

工作上覺得還蠻實用的程式片段。

struct task_struct *p;  
struct siginfo info;

pid_t pid = -1;

for_each_process(p) {
if (!strcmp(p->comm, "process name")){
pid = p->pid;
break;
}
}


if(pid != -1){

if(p != NULL) {
info.si_signo = SIGUSR1;
info.si_code = SI_QUEUE;
info.si_int = 0x3;
send_sig_info(SIGUSR1, &info, p);

printk(KERN_EMERG "Send SIGUSR1 with value %d to %s(%d)\n", info.si_int, "process name", pid);
}

}

Netlink 使用方法

Kernel Korner - Why and How to Use Netlink Socket這篇寫得非常好,關於netlink的使用方法,範例也非常簡潔易懂。

這裡就貼上關於不使用group方式process送訊息至kernel。

User Space

#include <sys/socket.h>
#include <linux/netlink.h>

#define MAX_PAYLOAD 1024 /* maximum payload size*/
struct sockaddr_nl src_addr, dest_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
int sock_fd;

void main() {
sock_fd = socket(PF_NETLINK, SOCK_RAW,NETLINK_TEST);

memset(&src_addr, 0, sizeof(src_addr));
src__addr.nl_family = AF_NETLINK;
src_addr.nl_pid = getpid(); /* self pid */
src_addr.nl_groups = 0; /* not in mcast groups */
bind(sock_fd, (struct sockaddr*)&src_addr,
sizeof(src_addr));

memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.nl_family = AF_NETLINK;
dest_addr.nl_pid = 0; /* For Linux Kernel */
dest_addr.nl_groups = 0; /* unicast */

nlh=(struct nlmsghdr *)malloc(
NLMSG_SPACE(MAX_PAYLOAD));
/* Fill the netlink message header */
nlh->nlmsg_len = NLMSG_SPACE(MAX_PAYLOAD);
nlh->nlmsg_pid = getpid(); /* self pid */
nlh->nlmsg_flags = 0;
/* Fill in the netlink message payload */
strcpy(NLMSG_DATA(nlh), "Hello you!");

iov.iov_base = (void *)nlh;
iov.iov_len = nlh->nlmsg_len;
msg.msg_name = (void *)&dest_addr;
msg.msg_namelen = sizeof(dest_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

sendmsg(fd, &msg, 0);

/* Read message from kernel */
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
recvmsg(fd, &msg, 0);
printf(" Received message payload: %s\n",
NLMSG_DATA(nlh));

/* Close Netlink Socket */
close(sock_fd);
}

Kernel Space

struct sock *nl_sk = NULL;

void nl_data_ready (struct sock *sk, int len)
{
wake_up_interruptible(sk->sleep);
}

void netlink_test() {
struct sk_buff *skb = NULL;
struct nlmsghdr *nlh = NULL;
int err;
u32 pid;

nl_sk = netlink_kernel_create(NETLINK_TEST,
nl_data_ready);
/* wait for message coming down from user-space */
skb = skb_recv_datagram(nl_sk, 0, 0, &err);

nlh = (struct nlmsghdr *)skb->data;
printk("%s: received netlink message payload:%s\n",
__FUNCTION__, NLMSG_DATA(nlh));

pid = nlh->nlmsg_pid; /*pid of sending process */
NETLINK_CB(skb).groups = 0; /* not in mcast group */
NETLINK_CB(skb).pid = 0; /* from kernel */
NETLINK_CB(skb).dst_pid = pid;
NETLINK_CB(skb).dst_groups = 0; /* unicast */
netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT);
sock_release(nl_sk->socket);
}

2012年7月12日 星期四

ipv6 pton空字串

當inet_pton進一個空字串再反向inet_ntop回來時,值會變怎樣呢?
範例程式碼如下
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
int main()
{
    unsigned char a[16];
    char b[64] = "";
    char c[64] = "";
    
    
    inet_pton(PF_INET6, b, &a);
    inet_ntop(PF_INET6, b, c, sizeof(c));
    
    fprintf(stderr, "c is %s\n", c);
    
    return 0;
}

結果:
./ntop_test 
c is ::

2012.7.12更新
在這個範例中inet_pton轉換空字串其實是失敗的,並沒有寫入a中。
其實::的結果是c為空字串轉換回去的結果。