從2011年工作到現在
大概也將近七年半了

應該來寫一些新的工作心得了
再找時間整理一下主題

今天一時興起
把之前買的 DIR-505 刷上最新版的 OpenWrt Chaos Calmer 15.05.1
把安裝的過程跟套件做個簡單的紀錄

1. 下載 OpenWrt 14.07 的 Firmware: openwrt-ar71xx-generic-dir-505-a1-squashfs-factory.bin
這邊由於 OpenWrt 15.05 的 Firmware 沒辦法直接透過 D-Link 的 WebUI 上傳上去,所以先 load 上一版的再升級XD
順利的話就可以看到 OpenWrt 的 WebUI

2. 下載 OpenWrt 15.05.1 的 Firmware: openwrt-15.05-ar71xx-generic-dir-505-a1-squashfs-factory.bin
下載完直接透過 OpenWrt 的 WebUI 更新即可

3. 切換為 Router Mode
OpenWrt 預設為 AP mode,但是我想設定成改成 Router Mode,所以需要做點修改
3-1. 從 Network -> Interface 進入設定頁面
3-2. 選擇 LAN 的 Edit -> Physical Settings,將 eth1 從 Bridge 移除掉
3-3. 回到 Network -> Interface,點選 Add Interface,命名為 WAN
3-4. 選擇 DHCP & 選擇 eth1

4. 安裝需要的套件
由於 DIR-505 本身有 USB 功能,所以新增的套件目前都以 USB storage 為主
- usbutils
- mountd (自動掛載功能)
- luci-app-samba (SAMBA 設定頁,會自動安裝 Samba Server)
- vsftpd (我最愛用的 FTP)

把這些設定好後就有 USB storage 功能了,也支援常見的 SAMBA/FTP

整理一下目前裝在系統上的七個 vim plugins

註:安裝下列套件前必須先安裝這些套件 $sudo apt-get install vim exuberant-ctags git-core

  1. Vundle (https://github.com/gmarik/Vundle.vim)
    協助安裝 vim plugin 的好工具
    裝好 git 之後可以下這個命令下載 Vundle:
    git clone https://github.com/gmarik/Vundle.vim.git ~/.vim/bundle/Vundle.vim
    裝好之後可以將你想裝的 plugin 寫在 vimrc 裡面,如下:

    " ------ Vundle Settings ------- 
    
    " This setting is form http://blog.chh.tw/posts/vim-vundle/
    
    let iCanHazVundle=1
    let vundle_readme=expand('~/.vim/bundle/vundle/README.md')
    if !filereadable(vundle_readme)
    echo "Installing Vundle.."
    echo ""
    silent !mkdir -p ~/.vim/bundle
    silent !git clone https://github.com/gmarik/vundle ~/.vim/bundle/vundle
    let iCanHazVundle=0
    endif
    set rtp+=~/.vim/bundle/vundle/
    call vundle#rc()
    " ---------- Plugins ----------
    
    Bundle 'MarcWeber/vim-addon-mw-utils'
    Bundle 'garbas/vim-snipmate'
    Bundle 'wesleyche/SrcExpl'
    Bundle 'scrooloose/nerdtree'
    Bundle 'vim-scripts/taglist.vim'
    Bundle 'wesleyche/Trinity'
    "Bundle 'vivien/vim-addon-linux-coding-style' #optional
    
    " ------ Trinity setting ------
    
    " Open and close all the three plugins on the same time 
    
    nmap <F8>  :TrinityToggleAll<CR> 
    " Open and close the Source Explorer separately 
    
    nmap <F9>  :TrinityToggleSourceExplorer<CR> 
    " Open and close the Taglist separately 
    
    nmap <F10> :TrinityToggleTagList<CR> 
    " Open and close the NERD Tree separately 
    
    nmap <F11> :TrinityToggleNERDTree<CR> 
    

    再進去 vim 下 :BundleInstall 就可以把這些套件一次裝完囉!

  2. linuxsty.vim (https://github.com/vivien/vim-addon-linux-coding-style)
    Linux Kernel Coding Style 的檢查外掛
    裝這個是因為我自己的 coding style 比較偏向於這個....但是沒有完全 follow......
    裝好後我會關閉 80 行之類的限制,結果都拿來檢查句尾空白XD

  3. snipMate (https://github.com/garbas/vim-snipmate)
    懶人補齊工具,例如輸入 for + tab鍵,就會變出下面這堆東西

    for (i = 0; i < count; i++) {
    /* code */
    }
    
  4. NERDTree (https://github.com/scrooloose/nerdtree)
    可以在 vim 側邊弄出一個檔案瀏覽視窗,想一次編輯多個檔案的時候很方便~

  5. Taglist (http://www.vim.org/scripts/script.php?script_id=273)
    看 code 的好工具,可以在左邊先幫你列出 code 裡面的 function、variable、marco...等等
    可以利用這個工具快速移到 "指定" 的位置

  6. Source Explorer (https://github.com/wesleyche/SrcExpl)
    看名稱就知道是也看 code 用的.....XD
    它的功能是利用 tag 去抓到 function 或是 type 的定義,結合 ctags 效果不錯
    在 ubuntu 要裝 ctags 的話就下 sudo apt-get install exuberant-ctags

  7. Trinity (https://github.com/wesleyche/Trinity)
    這個最重要了,是用來整合上面三套 plugin 的 plugin
    包括 NERDTree、Taglist、Source Explorer
    開起來真的有上面寫的FU
    The Trinity plugin manages Source Explorer, Taglist and NERD Tree, and build them as a great IDE which works like the "Source Insignt".

公司有些 code 只能在 Ubuntu 8.04 上面 build,但是很多套件都是舊版,很不方便。
後來在某次包 GPL code 之後,手癢把裝在 VM 上面的系統升級到 10.04,發現居然可以 build 過,就把工作機一路升級到 12.04 了,今年 Ubuntu 又出新的 LTS 版本,所以再往上推到 14.04。

這算是今年第四次安裝了,前兩次都用 VM 做測試,確保舊 code 可以在新系統上 build 之後裝到實機上,這次順便寫下來做個紀錄。

  1. 安裝 Ubuntu 8.04 Server Edition (無需網路,另外用server版是為了減少套件數量)

  2. 確定電腦可連上網路,沒有的話就裝上 driver
    我的 USB 外接網卡 Planex UE-200TX-G2 一插上去就可以動了
    但是一定要拔下來再插上去才會抓到,害我花了很多時間 build driver....Orz

  3. Ubuntu 8.04 已經 EOL 了,套件庫移到 old-releases.ubuntu.com,所以要修改一下 /etc/apt/sources.list 的 mirror

    $sudo vim /etc/apt/sources.list
    

    把所有 http://[xxx].ubuntu.com 的 mirror 位址改成 http://old-releases.ubuntu.com

  4. 更新 apt list

    $ sudo apt-update
    
  5. 安裝 build tools

    $ sudo apt-get install bison build-essential automake autoconf flex gawk libc6-dev libc6 zlib1g zlib1g-dev sharutils intltool pkg-config subversion
    
  6. 安裝 toolchain (略)

  7. 試著 build 一次 code (略)

  8. 開始升級之路 (8.04 --> 10.04 --> 12.04 --> 14.04)
    下面這個指令要下三次,升級後不可移除舊套件,不然會導致無法 build 舊 code

    $sudo do-release-upgrade -p
    

    ps. 升級依次後可以將 source.list 的 mirror 改成 free.nchc.org.tw

  9. 升級到 14.04 之後,安裝 x-window

    $sudo apt-get install lubuntu-desktop
    
  10. 修正網路卡代號 (因為我的網路卡是外接的...內建的抓到會變成 eth1)

    $sudo vim /etc/udev/rules.d/70-persistent-net.rules
    
  11. 裝舊版 GNU Patch v2.6.1
    http://ftp.gnu.org/gnu/patch/patch-2.6.1.tar.bz2

  12. 再次 build 一次 code 測試是否正常 (略)

  13. 安裝軟體 and 設定環境 (略)

  14. 更改 Ubuntu 預設 shell

    $sudo dpkg-reconfigure dash
    

    進去之後選 "no"

  15. 備份 (我的系統很容易被我自己玩壞XDDD)

Google 好幾下才找到這篇~~
先寫起來...之後會用到

OS: Ubuntu 12.10

$sudo ifconfig wlan0 down
$sudo iwconfig wlan0 mode managed
$sudo ifconfig wlan0 up
$sudo iwconfig wlan0 channel 6
$sudo ifconfig wlan0 down
$sudo iwconfig wlan0 mode monitor
$sudo ifconfig wlan0 up

另外要解析內容的話可以看這個 -> http://wiki.wireshark.org/HowToDecrypt802.11

資料來源:
http://ask.wireshark.org/questions/5826/cant-check-monitor-mode-checkbox-on-ubuntu

這兩天把一個純 IPv4 的 socket client 改成 Dual-Stack (IPv4/IPv6 通用)
很幸運地一下子就可以work了....XD

修改的部分:
function: gethostbyname() --> getaddrinfo()
structure: honest --> addrinfo

struct hostent {
    char  *h_name;            /* official name of host */
    char  **h_aliases;        /* alias list */
    int   h_addrtype;         /* host address type */
    int   h_length;           /* length of address */
    char  **h_addr_list;      /* list of addresses */
};
struct addrinfo {
    int              ai_flags;        /* customize behavior */
    int              ai_family;       /* address family */
    int              ai_socktype;     /* socket type */
    int              ai_protocol;     /* protocol */
    socklen_t        ai_addrlen;      /* length in bytes of address */
    struct sockaddr *ai_addr;         /* address */
    char            *ai_canonname;    /* canonical name of host */
    struct addrinfo *ai_next;         /* next in list */
};

Code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

int main(int argc, char *argv[]) {
    int sock = -1;
    int ret = -1;
    char buffer[1024] = {};
    char *result = NULL;
    struct timeval timeval;
    struct addrinfo addrinfo, *res, *r;
    fd_set afdset;

    if (argc < 3 || atoi(argv[2]) <= 0) {
        fprintf(stderr, "please input [hostname] and [port]\n");
        fprintf(stderr, "ex: %s %s %s\n", argv[0], "www.google.com", "80");
        exit(-1);
    }

    memset(&addrinfo, 0, sizeof(addrinfo));
    addrinfo.ai_family = AF_UNSPEC;
    addrinfo.ai_socktype = SOCK_STREAM;
    addrinfo.ai_protocol = IPPROTO_TCP;

    ret = getaddrinfo(argv[1], argv[2], &addrinfo, &res);
    if (ret != 0) {
        fprintf(stderr, "Failed to resolve address information\n");
        goto out;
    }

    for (r = res; r != NULL; r = r->ai_next) {
        sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
        ret = connect(sock, r->ai_addr, r->ai_addrlen);
        if (ret == 0) {
        if (r->ai_family == AF_INET6)
        printf("connected via IPv4\n")
            else if (r->ai_family == AF_INET6)
        printf("connected via IPv6\n")
            break;
    }
    }

    if (ret < 0) {
        fprintf(stderr, "connect() fail\n");
        freeaddrinfo(res);
        goto out;
    }

    snprintf(buffer, sizeof(buffer), "messages");

    if (send(sock, buffer, strlen(buffer), 0) < 0) {
        fprintf(stderr, "send() fail");
        goto out;
    }

    FD_ZERO(&afdset);
    FD_SET(sock, &afdset);
    timeval.tv_sec = 3;
//  timeval.tv_usec = 0;
    printf("%f" ,timeval.tv_usec);

    ret = select(sock + 1, &afdset, NULL, NULL, &timeval);
    if (ret == -1) {
        fprintf(stderr, "select() fail\n");
        goto out;
    }

    memset(buffer, 0, sizeof(buffer));
    if(FD_ISSET(sock, &afdset)) {
        read(sock, buffer, sizeof(buffer));
    } else {
        fprintf(stderr, "recv_buf = NULL\n");
        goto out;
    }

    printf("recv: %s \n", buffer);

out:
    return 0;
}

Reference:
1. http://blog.chinaunix.net/uid-26583794-id-3167485.html
2. http://pubs.opengroup.org/onlinepubs/7908799/xns/netdb.h.html
3. http://owend.corp.he.net/ipv6/sample_application/c/v6_client.c

有點古怪的 structure >____<
之前一直 try 都會 segmentation fault
必須先用 localtime()time_t 的變數存進去後....再放進特定的數值(不然還是會 segmentation fault.....XD)

值放好之後....要再利用 mktime() 把 structure tm 的值轉回 time_t
才能用相關的function (例如 difftime())做處理

另外這個 tm 資料結構有兩個部分比較特別

  1. tm_year 是從 1900 開始算起,所以放值的時候記得要先減1900
  2. tm_mon 範圍是 0~11
#include <stdio.h>
#include <time.h>

int main(void) {

    time_t t;
    struct  tm *timeinfo;
    int year = 2013;
    int mon = 12;
    int day = 31;
    int hour = 23;
    int min = 59;
    int sec = 59;

    timeinfo = localtime(&t);            /* Initial struct tm */
    timeinfo->tm_year    = year - 1900;  /* tm_year start with 1900 */
    timeinfo->tm_mon = mon - 1;      /* 0~11 */
    timeinfo->tm_mday    = day;
    timeinfo->tm_hour    = hour;
    timeinfo->tm_min = min;
    timeinfo->tm_sec = sec;

    t = mktime(timeinfo);           /* Convert structure tm to time_t */
    printf("%s", ctime(&t));

    return 0;
}

Reference: http://www.cplusplus.com/reference/ctime/mktime/

直接用 apt-get 安裝 openvpn
# apt-get install openvpn easyrsa

把 /usr/share/doc/openvpn 下面的 config 複製到 /etc/openvpn
# cp -R /usr/share/doc/openvpn/examples/easy-rsa/2.0 /etc/openvpn/easy-rsa
# zcat /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz > /etc/openvpn/server.conf

進入 /etc/openvpn/easy-rsa 並設定環境變數
# cd /etc/openvpn/easy-rsa
# source ./vars

接下來要開始製作 server 與 client 所需要的 key & certificate
清除 easy-rsa/key 目錄相關的檔案
# ./clean-all

建立憑證
# ./build-ca

建立 Server 的 Key 和 憑證
# ./build-key-server server

建立給使用者連線的 key
# ./build-key user1
# ./build-key user2 (optional)

建立加密金鑰
# ./build-dh

以下這幾個檔案必須放在 Server 的目錄(這邊是放在預設的目錄 /etc/openvpn/ )

  • ca.crt
  • ca.key
  • server.crt
  • server.key
  • dh1024.pem

# cd /etc/openvpn/easy-rsa/keys
# mv ca.crt ca.key server.crt server.key dh1024.pem /etc/openvpn/

另外 client 需要的檔案有下列這些(以user1為例)

  • ca.crt
  • user1.crt
  • user1.key

上面這三個檔案要複製給 user1 所在的電腦才能夠登入
Windows 預設 config 目錄為 C:\Program Files\OpenVPN\config
Linux 預設 config 目錄為 /etc/openvpn/

剩下的就是到 sample-config 目錄裡找到 client 的設定檔....改好之後放到上面預設的config目錄
完成之後就可以順利連到server了!

資料來源:

  1. OpenVPN HOWTO
  2. 免費的OPENVPN,建立你的專屬區網

之前有想寫一個 FQDN 轉 IP 的東西
用了 gethostbyname() 去做,不過這個 function 只適用 IPv4
輸入了 v6 相關的位址,會直接吐一個 segmentation fault 出來@@
所以改用 getaddrinfo() 跟 getnameinfo 來做相關的功能
並且可以根據需求設定只取得 IPv4 or IPv6 的位址

程式碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>

int main(int argc, char *argv[]) {

    struct addrinfo hints, *result;
    char hostname[1024] = "";
    int ret;
    
    if (argc < 2) {
        fprintf(stderr, "Usage: %s host msg...\n", argv[0]);
        goto fail;
    }

    memset(&hints, 0, sizeof(hints));
    memset(result, 0, sizeof(result));
    hints.ai_family = AF_UNSPEC;    // AF_UNSPEC, AF_INET, and AF_INET6
    hints.ai_socktype = SOCK_DGRAM;
    
    ret = getaddrinfo(argv[1], NULL, &hints, &result);

    if (ret != 0) {
        fprintf(stderr, "%s.\n", gai_strerror(ret));
        goto fail;
    }

    while (result != NULL) {
        memset(hostname, 0, sizeof(hostname));
        ret = getnameinfo(result->ai_addr,
                  result->ai_addrlen,
                  hostname,
                  sizeof(hostname),
                  NULL,
                  0,
                  NI_NUMERICHOST);

        if (ret == 0) {
            if (result->ai_family == AF_INET)
                printf("IPv4 ---> %s\n", hostname);
            else if (result->ai_family == AF_INET6)
                printf("IPv6 ---> %s\n", hostname);
        }

        result = result->ai_next;
    }

    return 0;

fail:
    return -1;
}



執行結果:
(1) ./a.out www.google.com
IPv4 ---> 173.194.72.99
IPv4 ---> 173.194.72.103
IPv4 ---> 173.194.72.104
IPv4 ---> 173.194.72.105
IPv4 ---> 173.194.72.106
IPv4 ---> 173.194.72.147
IPv6 ---> 2404:6800:4008:c01::93

(2) ./a.out www.google.com ipv4
IPv4 ---> 173.194.72.99
IPv4 ---> 173.194.72.103
IPv4 ---> 173.194.72.104
IPv4 ---> 173.194.72.105
IPv4 ---> 173.194.72.106
IPv4 ---> 173.194.72.147

(3) ./a.out www.google.com ipv6
IPv6 ---> 2404:6800:4008:c01::93


參考資料:
http://my.oschina.net/u/158589/blog/62896

剛才寫了一個簡單的多執行序程式....記錄一下

程式碼:

#include <stdio.h>
#include <pthread.h>

void* thread0(void*);
void* thread1(void*);

int main(void) {

        void* result;
        pthread_t t0; 
        pthread_t t1; 

        pthread_create(&t0, NULL, thread0, NULL);
        pthread_create(&t1, NULL, thread1, NULL);

        /* wait for thread terminate */
        pthread_join(t0, &result);
        pthread_join(t1, &result);

        return 0;
}

void* thread0(void *argu)
{
        int i = 0;
        for (; i<5; i++) {
                printf("thread0: %d\n", i); 
                sleep(1);
        }   
        return NULL;
}


void* thread1(void *argu)
{
        int i = 0;
        for (; i<10; i++) {
                printf("thread1: %d\n", i); 
                sleep(1);
        }   
        return NULL;
}

ps. Compile 時需要加上 -lpthread --> gcc a.c -lthread


執行結果:

$ ./a.out
thread1: 0
thread0: 0
thread1: 1
thread0: 1
thread1: 2
thread0: 2
thread1: 3
thread0: 3
thread1: 4
thread0: 4
thread1: 5
thread1: 6
thread1: 7
thread1: 8
thread1: 9

延伸閱讀:

  1. POSIX線程(pthread)入門文章分享
  2. Linux下的多線程編程
  3. pthread 執行緒函式庫
  4. POSIX Threads Programming