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為空字串轉換回去的結果。

2012年5月25日 星期五

ipv6位址比較

ipv4因為sin_addr中s_addr結構為uint32,可以利用"等於"直接作位址比較。

typedef uint32_t in_addr_t;
struct in_addr {
in_addr_t s_addr; /* IPv4 address */
};

比較方式可以像下面這樣。

(sa->sin_addr.s_addr != (((struct sockaddr_in *)&remote)->sin_addr.s_addr))

但是v6中sin6_addr就長得不太一樣了。

struct in6_addr {
union {
uint8_t u6_addr8[16];
uint16_t u6_addr16[8];
uint32_t u6_addr32[4];
} in6_u;

    #define s6_addr in6_u.u6_addr8
    #define s6_addr16 in6_u.u6_addr16
    #define s6_addr32 in6_u.u6_addr32
};

可以看到是使用陣列方式儲存。比較的話要使用memcmp方式來達成。

memcmp(sa_v6->sin6_addr.s6_addr, (((struct sockaddr_in6 *)&remote)->sin6_addr.s6_addr), 16) != 0

這個問題的來源可以參考Comparing a peer's IPv6 address with localhost

2012年5月24日 星期四

Porting applications to IPv6 HowTo

這次是要在linux下將ipv4的程式改成ipv4/ipv6相容的程式。

原文可以看Porting applications to IPv6 HowTo

首先利用sockaddr_storage取代傳統的sockaddr_in。當使用ipv4做溝通時,就將這個struct轉為sockaddr_in,使用ipv6溝通時,就要轉換為sockaddr_in6。sockaddr_storage裏面構造長得很像sockaddr_in和sockaddr_in6。

#if ULONG_MAX > 0xffffffff
# define __ss_aligntype __uint64_t
#else
# define __ss_aligntype __uint32_t
#endif
#define _SS_SIZE 128
#define _SS_PADSIZE (_SS_SIZE - (2 * sizeof (__ss_aligntype)))

struct sockaddr_storage
{
sa_family_t ss_family; /* Address family */
__ss_aligntype __ss_align; /* Force desired alignment. */
char __ss_padding[_SS_PADSIZE];
};

當我要使用的時候,我可以預先做轉好的動作備用。

struct sockaddr_storage sa;
struct sockaddr_in *sa_v4 = (struct sockaddr_in *)&sa;
struct sockaddr_in6 *sa_v6 = (struct sockaddr_in *)&sa;

或是你需要用的時候再做轉換。

(((struct sockaddr_in *)&sa)->sin_addr.s_addr)     --->v4
(((struct sockaddr_in6 *)&sa)->sin6_addr.s6_addr) --->v6

接著,記得系統要build好ipv6的module,那樣的話,bind, recv這些通訊方法通通不用改。


而要分辨現在溝通模式是v4還是v6,可以看ss_family。

if(sa.ss_family == AF_INET){
...
}else if(sa.ss_family == AF_INET6){
...
}

最後就要有耐心的補完v6的邏輯。因為網路位址轉字串,位址比較都和v4不相同。詳細範例可以再看前面提到的Porting applications to IPv6 HowTo