Sunday, September 21, 2008

making a 3G Huawei 220 USB modem work in Linux 2.6

Context: just received a Huawei 220 3G USB modem [1] (HSDPA/UMTS/EDGE/GPRS/GSM, HSDPA <= 3.6Mbps ), have to make it work in my Linux thinkpad X60 w/ Linux 2.6.20+ , for accessing the Swisscom HSDPA service.

It didn't work out of the box, mainly because of the device's CD emulation storage (windoze drivers :P) interfering with its comm device.

First searches return pointers to a "vodafone-mobile-connect-card-driver-for-linux" [2] package, which is absolute crap: a heavy weight GUI interface that doesn't even work (at least for me).

Hey ... I just want to have my /dev/ttyUSB0 to be able to configure a pppd on it, I found a true, low level solution [3] that manages to make the device behave like the very thing it was born for :)

Here is an excerpt of the mininum required configs to make it work

  • /etc/udev/rules.d/99-huawei.rules

    ## /etc/udev/rules.d/99-huawei.rules
    ##
    ## to enable it, run: udevcontrol reload_rules
    ##
    #
    # udev rule for HUAWEI E220 3G HSDPA Modem
    # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    # (c)opyleft OOZIE < oozie@poczta.fm >
    #
    # This file prevents the pseudo scsi cdrom device from enabling,
    # thus providing a workaround for kernel versions < 2.6.20
    #
    # Minor edition by JuanJo Ciarlante
    ##

    SUBSYSTEM=="block", \
    ACTION=="add", \
    SYSFS{idVendor}=="12d1", \
    SYSFS{idProduct}=="1003", \
    RUN="/sbin/modprobe usbserial vendor=0x12d1 product=0x1003", \
    OPTIONS="ignore_device"


  • /etc/ppp/peers/3g

    # /etc/ppp/peers/3g
    # pppd call 3g nodetach
    # (using Huawei e220 USB modem)
    /dev/ttyUSB0
    #3600000 #irrelevant
    noipdefault
    defaultroute
    user gprs
    password gprs
    #persist
    lock
    noauth
    nodetach
    usepeerdns
    nodeflate
    nobsdcomp
    nopcomp
    noaccomp
    novj
    novjccomp
    nomagic
    #asyncmap 0
    #lcp-echo-failure 10000
    #lcp-echo-interval 1000
    lcp-echo-failure 10
    lcp-echo-interval 30
    connect '/usr/sbin/chat -v -f /etc/ppp/peers/3g.chat'
    debug
    idle 180
    holdoff 10

  • /etc/ppp/peers/3g.chat

    # /etc/ppp/peers/3g.chat
    TIMEOUT 3
    ABORT BUSY
    ABORT 'NO CARRIER'
    ABORT VOICE
    ABORT 'NO DIALTONE'
    ABORT 'NO DIAL TONE'
    ABORT 'NO ANSWER'
    ABORT DELAYED
    "" ATZ
    OK ATQ0V1E1S0=0&C1&D2
    OK AT+CGDCONT=1,"IP","gprs.swisscom.ch","0.0.0.0",0,0
    OK ATDT*99#
    CONNECT ""


Friday, September 19, 2008

@Kirk McKursick's FreeBSD Internals course

I had the fortunate pleasure to attend Kirk McKursick's FreeBSD internals course, kindly $upported by Google Switzerland, my employer =).



reeBSD_internals_book.jpg


McKursick-May_the_source_be_with_you.jjo.jpg
May the source be with you ... `:-)


One interesting discussion I had with Kirk was about the behavior of zombie processes with respect to opened sockets and TCP data lingering: he stated that a process could be in zombie state while pushing the data ashes thru an already opened TCP socket ... something that I couldn't agree with, mainly because this would imply a high wait() "latency" in under a pkt loss/congestion scenario.

Linux even has a tcp_max_orphans sysctl explicitly there for this (man 7 tcp), anyhow I wanted to be sure about the behavior of Linux and xBSD, so I coded a quick&dirty test, see zocket.c and Makefile below.


Result: as expected, the sockets were orphaned after the process became zombie, and remained as is (FIN_WAIT1) independently from the process path to death =) (tested on Linux, FreeBSd and OpenBSD).

As a side note, was interesting to find that in Linux you can push a BPF down to filter the very data stream, but in FreeBSD you can only hook at the accept() syscall with another mechanism (man 9 accept_filter), this is somewhat 2-sided for Linux: cool that you can arbitrary filter the data stream, notso-cool that you can easily create a local DoS with this power :)

make && make test ## should do the magic:


  • zocket.c
    /*
    * zocket.c: connect a socket, block its data stream, write data @children:
    * show zombie and socket lifetime afterwards
    *
    * Author: JuanJo: jjo () google com
    * License: GPLv2+
    */

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <assert.h>
    #if __linux__
    #include <linux/filter.h>
    #endif

    /* quickie wrapper over syscall error checking */
    #define ERR_IF(cond) do { if(cond) { perror( #cond ); abort(); } } while (0)

    static int block_fromto(int sockfd, const struct sockaddr_in *sp,
    const struct sockaddr_in *dp)
    {
    #ifdef __linux__
    /*
    * Push a BPF into _this_ socket only, interesting enough
    * this seems to be a linux-only feature, BSD has this available
    * only at accept().
    */

    struct sock_filter bpf_blockme[]= {
    BPF_STMT(BPF_RET+BPF_K, 0), /* just accept 0 bytes ;) */
    };
    struct sock_fprog filter = {
    sizeof(bpf_blockme)/sizeof(*bpf_blockme), bpf_blockme,
    };

    ERR_IF(setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER,
    &filter, sizeof(filter))<0);
    #else /* assuming BSD: block at PF level */
    char cmd[1024];
    cmd[sizeof cmd-1]=0;
    snprintf(cmd, sizeof cmd-1,
    "echo block drop out inet proto tcp "
    "from %s port %d to %s port %d| pfctl -f-",
    /* leaking but effective ... */
    strdup(inet_ntoa(sp->sin_addr)), htons(sp->sin_port),
    strdup(inet_ntoa(dp->sin_addr)), htons(dp->sin_port));
    printf("====== configuring pf:\n+ %s\n", cmd);
    ERR_IF(system(cmd)<0);
    #endif
    return 0;
    }
    int main(int argc, const char *argv[]) {
    int sock;
    int pid;
    int syncpipe[2];
    unsigned n;
    char cmd[2048];
    struct sockaddr_in dest,me;

    #ifndef __linux__
    /* need to use /sbin/pfctl for blocking pkt stream if !linux */
    assert(getuid()==0);
    #endif
    if (argc != 3) {
    fprintf(stderr, "ERROR: Usage: %s <ip> <port>\n", argv[0]);
    exit(255);
    }
    dest.sin_family = AF_INET;
    ERR_IF( inet_aton(argv[1], &dest.sin_addr) == 0);
    dest.sin_port=htons(atoi(argv[2]));

    sock = socket(AF_INET, SOCK_STREAM, 0);
    ERR_IF(sock <0);
    ERR_IF(connect (sock, (struct sockaddr *)&dest, sizeof dest) < 0);
    n=sizeof me;
    getsockname(sock, (struct sockaddr *)&me, &n);

    /* unbuffer stdout */
    setbuf(stdout, NULL);
    /* block (output) data stream */
    block_fromto(sock, &me, &dest);
    pipe(syncpipe);
    for(n=3;n;n--) {
    switch(pid=fork()) {
    case 0:
    close(syncpipe[0]);
    write(sock, "1234567890", 10);
    _exit(0);
    case -1: ERR_IF(1);
    }
    }
    close(sock);
    /* sync to children death */
    read(syncpipe[0], NULL, 0);
    cmd[sizeof cmd -1]=0;
    snprintf(cmd, sizeof cmd -1,
    "ps -o pid,ppid,stat,command|egrep [z]ocket;"
    "netstat -tn|egrep '[.:]%d .*[.:]%d'",
    ntohs(me.sin_port), ntohs(dest.sin_port));
    /* this will show the zombies and the socket send-q (as netstat -tn) */
    printf("====== BEFORE wait() ======\n+ %s\n", cmd); system(cmd);
    while(wait(NULL)>0);
    /* obviously the zombies are gone, what about the (orphaned) socket ?*/
    printf("====== AFTER wait() ======\n+ %s\n", cmd); system(cmd);
    return 0;
    }

  • Makefile
    ## Makefile for zocket.c
    ## Tested on: Linux 2.6, FreeBSD 6.x(dragonfly), OpenBSD 4.3
    CFLAGS=-Wall -g
    T=zocket.bin.$(OS)
    DEST=10.255.255.1 111

    ## No portable (GNU,BSD) way of doing VAR=<output_from_shellcmd>, wrap
    ## them by invoking make again
    all:
    make OS=`uname -s` all_os
    test:
    make OS=`uname -s` test_os

    all_os: $(T)

    setup_pf:
    ## FreeBSD: dynload PF module
    -@test -x /sbin/kldload && \
    { /sbin/kldstat | egrep pf.ko || { kldload pf; sleep 1;};}
    ## Enable PF
    -@test -x /sbin/pfctl && \
    sudo /sbin/pfctl -e -f- </dev/null 2>/dev/null;exit 0

    test_os: all_os setup_pf
    @ulimit -c 0; [ $(OS) != Linux -a -x /usr/bin/sudo ] && SUDO=sudo;\
    exec $$SUDO ./$(T) $(DEST) | tee test.out.$(OS)

    $(T): zocket.c
    $(CC) $(CFLAGS) -o $(@) $(?)

    clean:
    rm -fv zocket.bin.*

    %.html: %
    c2html -s $(^)

  • Thursday, September 11, 2008

    LHC

    Unless you've been living under a rock [*], you've surely read ad-nauseum about the
    Large Hadron Collider ... having moved to Switzerland about a year ago, it seems not to be a very bold move to become a blackhole neighbor ... we'll see (well ... actually the oppositte ;)

    Cool that we have the chance to watch their live webcam:
    http://lhc.web.cern.ch/lhc/webcam.html

    [*] living under a rock won't save you either...