unix4fun

Aller au contenu | Aller au menu | Aller à la recherche

mercredi 9 octobre 2013

Un compresseur de fichier ? T'es Ouf, man !

Ok la blague du titre est naze, mais j'assume. Après une micro-discussion sur IRC, je me décide à programmer un chtit outil (en C, on se refait pas), qui fait de la compression/décompression de fichier. Super basique, le truc, il travaille entièrement en mémoire, gère pas le streaming de données, etc. Et en plus il a un ratio de compression à chier, mais c'est pas grave.

Le truc utilise l'algorithme naïf de huffman. Le code, c'est par ici !

Démonstration!

$ ll hosts*
-rw-r--r-- 1 poz poz 236 Oct  9 18:56 hosts
$ moizip hosts
$ ll hosts*
-rw-r--r-- 1 poz poz 236 Oct  9 18:56 hosts
-rw-r---- 1 poz poz 198 Oct  9 18:57 hosts.mz
$ moizip -d hosts.mz -o hosts.unpack
$ ll hosts*
-rw-r--r-- 1 poz poz 236 Oct  9 18:56 hosts
-rw-r--r-- 1 poz poz 198 Oct  9 18:57 hosts.mz
-rw-r--r-- 1 poz poz 236 Oct  9 18:57 hosts.unpack
$ diff hosts hosts.unpack 
$ for i in hosts hosts.unpack ; do md5sum $i ; done
0d0fc374ead17cd82f1bdd203dd4f04a  hosts
0d0fc374ead17cd82f1bdd203dd4f04a  hosts.unpack
$ 

Ah... ouais, le tool s'appelle moizip, faut regarder les choses en face :)

mercredi 10 juillet 2013

Un petit coup de main en cas de debug

Yo les gens.

Franchement, ça ne vous est jamais arrivé d'avoir codé un programme qui tourne, et puis parfois, *poof*, le truc explose en vol après un temps qui rend difficile le debug ? Je veux dire, c'est pas trop possible de lancer le binaire dans gdb ou valgrind, sinon il rame sa mère et on n'a pas 10 ans pour attendre le crash. Idem quand on le link avec la libefence.

Ben voilà. Un collègue avait émis l'idée de coder un shared object qu'on preload via LD_PRELOAD, et qui va invoquer gdb sur la réception de certains signaux. Pas de ralentissement, et en cas de crash on a directement un gdb qui est là, tout frais, et qui nous attend. C'est pratique non ?

Bon alors le code est pas super portable (dlopen sur la libc.6.so, les numéros des signaux écrits en dur dans le code), mais ça fait le taff. C'est par ici.

mercredi 3 avril 2013

CUDA pour piger les bases...

Je me prends la tete depuis qqes jours/semaines de temps en temps sur la doc CUDA,
tout n'est pas très clair, mais ça l'est devenu un peu plus grâce a ce lien :

http://www.caam.rice.edu/~timwar/RMMC/CUDA.html

Enjoy!

dimanche 23 décembre 2012

OS/X et BPF

rhalalal je perds parfois du temps en conneries, la j'en ai vraiment perdu beaucoup sur une bêtise...
Je ne pigeais pas pourquoi un read(bpf_fd, buffer, size) me retournait un EINVAL a chaque fois...

et j'ai juste relu religieusement bpf(4) et voila ce qu'on y trouve d'intéressant en étant un tout petit peu "attentif":

[...]
Reads from these files return the next group of packets that have matched the filter.  To improve per-
formance, the buffer passed to read __MUST BE THE SAME SIZE AS THE BUFFERS USED INTERNALLY BY BPF__.  This
size is returned by the BIOCGBLEN ioctl (see below), and can be set with BIOCSBLEN.  Note that an indi-
vidual packet larger than this size is necessarily truncated.
[...]

Et voila... c'est aussi bête que ça et ça marche... grmlbmlmb

jeudi 7 juin 2012

On est encore là, prêts à foutre le souk tout l'monde est cor-da !

Une petite news pour dire qu'on vit encore, et j'en profite pour vous vendre un tool bien sympathoche.

Un collègue de bureau m'a montré l'existence de cppcheck, et je dois dire que c'est pas mal du tout, ce truc.

C'est un outil d'analyse statique pour du C/C++. Il est intégré (ou intégrable) à des IDE, pour ceux qui s'en servent, pour plus de facilité d'utilisation.

Ça sort des rapports au format xml/html faciles à exploiter, il y a relativement peu de faux positifs, trouve aisément des memory leaks, etc.

Bref, une petite passe sur chaque projet aide à trouver des bugs en très peu de temps.

Ouala, c'est tout, enjoy ! :)

Edit : il y a aussi l'utilitaire de la suite clang, le bien nommé `scan-build'. Dans un repository il suffit de taper `scan-build make' puis `scan-view <nom du rapport>'. Ça marche bien et c'est un bon complément à cppcheck.

lundi 19 mars 2012

Android SDK, NDK, Kafka, Troika, des trucs en ..ka

Un tres simple introduction au NDK Android sur un billet sympathique et a lire pour ceux qui veulent s'y mettre! (et le cross compilo facile c'est par la aussi!! :))

https://www.synthetos.com/blog/native-embedded-code-on-android/

jeudi 16 février 2012

Stop! Hamming time!

Dans la même veine qu'un précédent ticket, j'avais besoin de faire une tâche particulière : calculer la distance de Hamming entre plusieurs fichiers, dans un objectif absolument top secret de ouf.

Contrairement à fdup(1), cette fois j'ai (rapidement) cherché dans les dépôts debian/ubuntu si un truc existait déjà. En regardant un peu kdiff3, xdelta, etc, j'ai rien trouvé de bien satisfaisant, alors je l'ai codé. C'est absolument bidon, je préviens, mais ptet que ça servira à quelqu'un, sait-on jamais...

C'est par ici : hamming.

lundi 13 février 2012

Ah ah ! Je ne suis pas dupe !

Bon, ce week end je me faisais passablement chier, et en explorant le contenu de mon $HOME je me suis rendu compte que c'était un peu le bordel, quand même. Des fichiers inutiles, des doublons, etc.

Du coup, illumination, le doigt du génie a effleuré mon front obtus l'espace d'un instant : et si je codais un truc-machin pour repérer tous les fichiers identiques sur ma machine ?!

Et voilà, fdup est né (c'est du C, parce que comme je m'embêtais je voulais pas faire un "one-liner" en shell avec find(1) + awk(1), faut bien s'occuper) ! C'est super minimaliste pour l'instant, hein, y'a genre 2-3 options qui se tirent la bourre, et pis c'est tout.

Je vous entends ricaner d'ici : bien sûr qu'il y a d'autres outils qui font le même boulot (fdupes(1), duff, y'a même un fdup déjà présent sur github -- bon, la description dans le README est un peu nawak mais on s'en fout !), mais ça, je m'en suis aperçu qu'après mon premier commit. Comme d'hab'.

mardi 18 octobre 2011

ahhh il reste des gens drôles! pfiiiouuu j'ai flippe!

En ces temps de gang bang d'ego généralisé, de benchmarking de performance sur l'échelle Ritchie (RIP) et autre courses a la réussite sociale, il reste des gens dont la carriere et la publication frénétique d'information pour voir son nombre de hits et ses fans augmenter toute en mimant le désintérêt et le detachement n'est pas au centre de leur vie et arrivent a s'amuser et surtout a avoir de l'auto derision, ca m'a fait hurler de rire alors que c'est completement debile, mais c'est pas grave, j'ai ris, ca fait du bien! donc je "share" :)

Sa premiere idee farfelue (hahahaha):

/*
** pam_happy_hour.c - PAM happy hour auth module. Solaris 2.6+
**      During happy hour passwords aren't required, any will do.
**
[...]

pam_happy_hour.c

Allez j'en pioche une autre dans le tas (:~)):

# maybe - companion to /usr/bin/true and /usr/bin/false.
#
# This should be installed in your system location, eg /usr/bin/maybe.
[...]

maybe

Et il en a fait pleins d'autre aussi farfelue a voir la:

huhuhuh

vendredi 7 octobre 2011

Mon Toy-project du moment ! Un FS sur le cloud !

Beh voilà, encore un peu d'auto-promotion. Et en fait j'ai un peu menti, ça fait plusieurs mois que je ne commit qu'un bout de code par-ci ou par-là.

Ma société a écrit une bibliothèque en open source pour travailler sur le cloud (différents protocoles supportés, mais je vous laisse découvrir ça). Bref, je me suis basé sur cette lib pour implémenter un filesystem en utilisant FUSE : dropletfs ; c'est lent (car le design est simpliste et pas optimisé), mais rigolo.

Toutes les features d'un FS posix ne sont pas présentes, loin de là (gérer les hardlinks est plutôt compliqué), mais pour un usage basique, ça 'juste marche'.

Une petite démo intéractive (nécessite le package ttyrec) : ici

jeudi 17 février 2011

Des hooks sympa de fonctions avec gcc

Yo yo yo.

J'avais une base de code, avec pas mal de fonctions déjà implémentées, et puis je me suis dit "ooooomm, faudrait que je trace le temps d'exécution de chacune de ces fonctions, pour savoir où concentrer mes efforts, en faisant telle et telle stat'". Bon alors je vous entends déjà barir au loin "hey mais pauvre naze, y'a plein d'outils de profiling", et c'est vrai. Seulement je voulais faire un truc à ma sauce d'une part (histoire de voir un peu comment ça fonctionne), et surtout pouvoir ajouter les stats qui m'intéressent, faire des trucs aux p'tits oignons quoi. Et puis c'est pas comme si ça prenait des jours à coder.

Alors après avoir cherché un peu, je tombe sur la doc GCC (même le man en parle, à vrai dire) :


void __cyg_profile_func_enter(void *func, void *callsite); 
void __cyg_profile_func_exit(void *func, void *callsite);


Ces fonctions sont appelées respectivement à l'entrée et la sortie de chacune des fonctions de votre programme. Il y a une linuxerie ensuite pour obtenir le nom de l'appelant, ie. passer par dladdr() ou __build_return_address(0). Ensuite vous stockez les infos comme vous voulez, perso j'utilise une hashtable dont les clefs sont les symboles (aka noms de fonctions), avec chaque cellule contenant des stats, sur le nombre d'appel, le temps d'exécution moyen, etc.

J'ai utilisé ça pour un code sur lequel je passe un peu de temps en ce moment, et la sortie ressemble à (le > correspond à l'entrée dans la fonction, le < à la sortie, faudrait rendre le truc plus joli avec un système de pile pour avoir la profondeur d'appel de chacune en fonction d'un décalage horizontal, mais bon, ça viendra après) :


[...]
1297941768.719531 > dfs_mkdir@0x40f534
1297941768.719607 > dfs_mkdir_timeout@0x41398f
1297941769.416415 < dfs_mkdir_timeout@0x41398f -- 696ms
1297941769.416445 < dfs_mkdir@0x40f534 -- 696ms
1297941769.416469 > dfs_getattr@0x40c16c
1297941769.416684 > dfs_namei_timeout@0x413ef7
1297941769.534098 < dfs_namei_timeout@0x413ef7 -- 117ms
1297941769.534161 > dfs_getattr_timeout@0x413690
1297941770.104592 < dfs_getattr_timeout@0x413690 -- 570ms
1297941770.104874 < dfs_getattr@0x40c16c -- 570ms

                                -- report --
symbol dfs_getattr_timeout: #calls: 46, average call duration: 266ms
symbol dfs_opendir_timeout: #calls: 1, average call duration: 780ms
symbol dfs_namei_timeout: #calls: 47, average call duration: 299ms
symbol dfs_chdir_timeout: #calls: 1, average call duration: 0ms
symbol dfs_mkdir: #calls: 1, average call duration: 696ms
symbol dfs_getattr: #calls: 48, average call duration: 275ms
symbol dfs_mkdir_timeout: #calls: 1, average call duration: 696ms


Le code (crados, pas encore nettoyé) est visible ici. Comme je disais, le truc vraiment sympa c'est juste d'avoir à appeler profile_init() et profile_fini() dans mon code. Et encore, on peut faire mieux en générant un .so qu'on charge avec LD_PRELOAD, histoire d'avoir vraiment 0 code impacté. Faut juste compiler les objets avec -fPIC et -finstrument-functions. That's all folks.

OK c'est un profiler du pauvre, mais ici à unix4fun, on est prolos. Et on vous emmerde !

mardi 21 décembre 2010

Pour les addicts du sniffing réseau: Junkie

Un peu de proselytisme ne fait jamais de mal, alors voilà Junkie, un sniffer releasé en Open Source par la société SecurActive.

Pour l'instant il a été programmé pour tourner sur du Linux debian-like, donc...


Un extrait du README:


Compared to previously available tools junkie lies in between tcpdump and
wireshark. Unlike tcpdump, its purpose is to parse protocols of any depth;
unlike wireshark, through, junkie is designed to analyze traffic in real-time
and so cannot parse traffic as completely as wireshark does.

In addition, junkie's design encompasses extendability and speed:

- plug-in system + high-level extension language that eases the development and
  combination of new functionalities;
- threaded packet capture and analysis for handling of high bandwidth network;
- modular architecture to ease the addition of any protocol layer;
- based on libpcap for portability;
- well tested on professional settings.


Il manque un "IPv6 compliant" je pense, dans ce README.

Seuls les parseurs sont releasés en Open Source, mais c'est déjà bien sympa. Le design est fait de telle sorte qu'il est facile d'ajouter un plugin d'analyse pour un protocole particulier 1. Et pour ceux qui n'aiment pas le C, il y a possibilité d'exercer vos talents de codeurs Scheme (guile plus précisemment), car il est scriptable à l'aide de ce langage ! Ouh ouh !


Un petit exemple d'utilisation ? OK, c'est parti :


$ sudo ./src/junkie -i wlan0 -c config/start-repl.scm
2010-12-22 00:36:45: J-main: log.c/log_set_file: Opening log file.


OK, là on lui a dit de loguer sur stdout, d'écouter l'interface wireless wlan0, et de prendre le fichier de conf par défaut qui active le REPL. Donc, dans un autre terminal, je vais me connecter sur la machine, en tcp/29000 pour ajouter des settings sur la mouche comme disent nos amis anglais (ie. "on the fly"... ok c'est nul).


$ rlwrap telnet localhost 29000
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
junkie> (set-iface-filter "wlan0" "icmp")
#t
junkie> (set-log-level 6)
#<unspecified>
junkie>


Les valeurs de retour affichées sont celles de guile, à savoir #t pour true, et #<unspecified> pour dire qu'il n'y a pas de code de retour. C'est un peu curieux au début, mais ceux qui ont utilisé ce langage ne seront pas dépaysés.

Là je lui ai demandé de prendre en compte le filtre BPF (tcpdump, vous connaissez, hein ?) "icmp" pour l'interface wlan0.

Hey ouais, c'est ça qui est cool, on peut écouter sur différentes interfaces avec des filtres différents. Bref, dans un autre terminal je tape :


$ ping free.fr -c1
PING free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_seq=1 ttl=118 time=37.1 ms

--- free.fr ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 37.176/37.176/37.176/0.000 ms


Et si on repasse dans la console de log, on a :


Capture@0x23f0068: head_len=48, payload=60, dev_id=0, tv=1293009623s 788239us
Ethernet@0x2413ea8: head_len=14, payload=46, vlan_id=0, source=00:1b:2f:2e:e0:fa, dest=ff:ff:ff:ff:ff:ff, proto=2054

Capture@0x23f0068: head_len=48, payload=74, dev_id=0, tv=1293009880s 139307us
Ethernet@0x2413ea8: head_len=14, payload=60, vlan_id=0, source=00:1c:bf:9d:2e:21, dest=00:15:e9:f2:30:67, proto=2048
IPv4@0x2414e38: head_len=20, payload=40, version=4, addr=10.0.63.10->82.237.175.5, proto=6, ttl=64
TCP@0x23bcf08: head_len=40, payload=0, ports=42007->1234, flags=Syn, win=5840, ack=0, seq=902941316

Capture@0x23f0068: head_len=48, payload=98, dev_id=0, tv=1293009631s 741103us
Ethernet@0x2413ea8: head_len=14, payload=84, vlan_id=0, source=00:1c:bf:9d:2e:21, dest=00:15:e9:f2:30:67, proto=2048
IPv4@0x2414e38: head_len=20, payload=64, version=4, addr=10.0.63.10->212.27.48.10, proto=1, ttl=64
ICMP@0x23bb198: head_len=64, payload=0, type=EchoRequest, err=NONE

Capture@0x23f0068: head_len=48, payload=98, dev_id=0, tv=1293009631s 744391us
Ethernet@0x2413ea8: head_len=14, payload=84, vlan_id=0, source=00:15:e9:f2:30:67, dest=00:1c:bf:9d:2e:21, proto=2048
IPv4@0x2414e38: head_len=20, payload=64, version=4, addr=212.27.48.10->10.0.63.10 (hashed the other way), proto=1, ttl=122
ICMP@0x23bb198: head_len=64, payload=0, type=EchoReply, err=NONE

Un lecteur attentif verra qu'il n'y a pas que de l'ICMP qui a été logué (cf. les premières lignes). Il a raison ! Mais comme je l'ai dit plus haut, on a changé la config à chaud, donc Junkie a eu le temps de loguer quelques paquets avant de prendre en compte le filtre BPF.

Et voilà ! Bon, c'est un exemple parmi des tonnes hein, chacun peut y trouver son compte, là c'est vraiment pour expliquer le fonctionnement. Pour plus d'infos :

$ junkie -h

Ou bien pour des détails vraiment poussés, dans le REPL :


junkie> (help)
[...]
junkie> (help 'commande)



1 Tutorial de codage d'un parseur : parser implementation


jeudi 4 novembre 2010

Le strict aliasing en C

Un billet assez exhaustif, avec des exemples de code et d'output assembleur : c'est ici

jeudi 7 octobre 2010

Pwnage par libpcap

Yo. Dans ce post je vais expliquer comment je me suis battu pour faire un truc assez simple avec la libpcap sous linux.

Au début était un sniffer, je voulais écouter sur une interface à grands coups de SOCK_RAW + recvfrom(). À l'ancienne, quoi. L'idée était de ne pas utiliser de handler pcap (pcap_t *). Au bout d'un moment je me dis "tiens, ça serait sympa d'ajouter les filtres BPF comme dans tcpdump". Brillante idée, non ? Non, ok. Bref, le code de départ est le suivant :

// -*- c-basic-offset: 4; indent-tabs-mode: nil -*-
// vim:sw=4 ts=4 sts=4 expandtab
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
#include <arpa/inet.h>
#include <netpacket/packet.h>
#include <poll.h>
#include <linux/types.h>
#include <errno.h>
#include <pcap.h>
#include <linux/filter.h>

#define FRAME_SIZE 65536

/* iface_setprom() sets "device" in promiscuous mode */
static int
iface_setprom(int sock_fd, const char *device)
{
    struct ifreq ifr;

    memset(&ifr, 0, sizeof ifr);
    snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", device);

    if (ioctl(sock_fd, SIOCGIFFLAGS, &ifr) == -1) {
        printf("[%s] ioctl: %s.\n", device, strerror(errno));
        return -1;
    }

    ifr.ifr_flags |= (IFF_PROMISC | IFF_UP);

    if (ioctl(sock_fd, SIOCSIFFLAGS, &ifr) == -1) {
        printf("[%s] ioctl: %s.\n", device, strerror(errno));
        return -1;
    }

    printf("[%s] enter in promiscuous mode\n", device);
    return 0;
}



À partir d'une chaîne de caractère entrée, par exemple "tcp and port 22", on veut obtenir une structure comprise par pcap_compile*(). Comme dit précédemment, je ne veux pas de pcap_t* à manipuler, je vais donc utiliser pcap_compile_nopcap() (non mentionné dans le man de pcap_compile sous linux, évidemment).

/* iface_setfilter() applies a LSF filter to the file descriptor */
static int
iface_setfilter(int sock_fd, const char * const device, const char * const filter)
{
    if (!filter || !*filter) {
        printf("[%s] no filter to install\n", device);
        return 0;
    }

    struct bpf_program bpf;
    memset(&bpf, 0, sizeof bpf);

    char errbuf[PCAP_ERRBUF_SIZE = "";

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof bpf)) {
        printf("[%s] setsockopt: %s (filter '%s')\n", device, strerror(errno), filter);
        return -1;
    }

    printf("[%s] filter '%s' successfully installed\n", device, filter);
    return 0;
}

/* iface_init() is the main device initialization function */
static int
iface_init(struct pollfd *fds, const char * const device, const char * const filter)
{
    printf("Initializing interface %s (filter '%s')\n", device, filter);

    fds->fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
    if (fds->fd == -1) {
        printf("[%s] socket: %s. Exiting...\n", device, strerror(errno));
        exit(EXIT_FAILURE);
    }

    fds->events = POLLIN | POLLPRI;

    if (0 != iface_setprom(fds->fd, device)) return -1;
    if (0 != iface_bind(fds->fd, device)) return -1;
    if (0 != iface_setfilter(fds->fd, device, filter)) return -1;

    return 0;
}

static void
frame_manager(const char * const device, struct pollfd *fd, struct timeval const *now)
{
    uint32_t i;

    if ((fd->revents & (POLLIN | POLLPRI)) == 0)
        return;

    unsigned char buf[1500] = "";
    ssize_t n = recvfrom(fd->fd, buf, sizeof buf, MSG_DONTWAIT, NULL, NULL);
    if (n < 0) {
        printf("[%s] recvfrom: %s (i=%d, fd=%d)\n", device, strerror(errno), i, fd->fd);
        return;
    }

    printf("[%d,%d] packet received\n", (int)now->tv_sec, (int)now->tv_usec);
}

int main(int ac, char **av)
{
    char *filter = "";
    char *device = "dummy0";

    if (ac > 1) {
        filter = av[1];
    }

    struct pollfd fd;
    (void)iface_init(&fd, device, filter);

    while (/*CONSTCOND*/ 1) {
        if (poll(&fd, 1, -1) > 0) {
            struct timeval now;
            gettimeofday(&now, NULL);
            frame_manager(device, &fd, &now);
        }
    }

    close(fd.fd);
    return EXIT_SUCCESS;
}



Tout d'abord, je monte une interface virtuelle pour éviter le bruit généré par les paquets circulants sur mes vraies interfaces :

$ sudo modprobe dummy
$ sudo ifconfig dummy0 mtu 1600 up



Maintenant, si je compile et j'exécute le code, et que je balance du ssh sur dummy0, j'ai ça :

$ cc -o cap cap.c -lpcap
$ sudo ./cap "tcp and port 22"
[...]



Et dans un autre terminal :

$ sudo tcpreplay -i dummy0 ssh.pcap



Mais aucun "packet received" ne s'affiche. Damned ! En inspectant un peu le code, on voit ça :

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof bpf)) {



Ça a l'air bien comme ça, hein. Malheureusement, le 'struct bpf_program' n'est pas compris sous linux, qui, ne voulant pas faire comme ses potes *BSD attend un 'struct sock_fprog *' en avant dernier argument. Bon, ici je me dis "il doit bien y avoir une fonction qui fait la conversion, cherchons dans le source de libpcap". En jetant un coup d'oeil rapide, on tombe sur ça :

libpcap-0.9.8/pcap-linux.c:215 static int       fix_program(pcap_t *handle, struct sock_fprog *fcode);



Je suis content, mais visiblement je dois passer par un pcap_t. Du coup, je reprends le code en l'adaptant pour taper directement dans la structure interne du handler pcap, et j'écris la fonction suivante :

#define SLL_HDR_LEN 16
static int lcc = 0; // linux cooked capture

static int
fix_offset(struct bpf_insn *p)
{
    if (p->k >= SLL_HDR_LEN) {
        p->k -= SLL_HDR_LEN;
    } else if (p->k == 14) {
        p->k = SKF_AD_OFF + SKF_AD_PROTOCOL;
    } else {
        return -1;
    }
    return 0;
}

static int
fix_code(struct bpf_program *bpf, struct sock_fprog *fcode)
{
    size_t prog_size;
    register int i;
    register struct bpf_insn *p;
    struct bpf_insn *f;
    int len;

    len = bpf->bf_len;
    prog_size = sizeof(bpf->bf_insns) * len;
    f = malloc(prog_size);
    if (f == NULL) {
        printf("malloc: %s\n", strerror(errno));
        return -1;
    }
    memcpy(f, bpf->bf_insns, prog_size);
    fcode->len = len;
    fcode->filter = (struct sock_filter *)f;

    for (i = 0; i < len; ++i) {
        p = &f[i];
        switch (BPF_CLASS(p->code)) {

        case BPF_RET:
            if (BPF_MODE(p->code) == BPF_K) {
                if (p->k != 0)
                    p->k = 65535;
            }
            break;

        case BPF_LD:
        case BPF_LDX:
            switch (BPF_MODE(p->code)) {

            case BPF_ABS:
            case BPF_IND:
            case BPF_MSH:
                if (lcc) {
                    if (fix_offset(p) < 0) {
                        return 0;
                    }
                }
                break;
            }
            break;
        }
    }
    return 1;       /* success! */
}



Maintenant je réécris ma fonction iface_setfilter() pour convertir le code BPF :

static int
iface_setfilter(int sock_fd, const char * const device, const char * const filter)
{
    if (! filter || ! *filter) {
        printf("[%s] no filter to install\n", device);
        return 0;
    }

    struct bpf_program bpf;
    memset(&bpf, 0, sizeof bpf);

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }

    struct sock_fprog fcode;
    memset(&fcode, 0, sizeof fcode);
    int ret = fix_code(&bpf, &fcode);

    if (1 != ret) {
        printf("[%s] fix_code failed (rcode = %d) for filter '%s'\n", device, ret, filter);
        return -1;
    } else {
        printf("[%s] fix_code worked (rcode = %d) for filter '%s'\n", device, ret, filter);
    }

    if (-1 == setsockopt(sock_fd, SOL_SOCKET, SO_ATTACH_FILTER, &fcode, sizeof fcode)) {
        printf("[%s] setsockopt: %s (filter '%s')\n", device, strerror(errno), filter);
        return -1;
    }

    printf("[%s] filter '%s' successfully installed\n", device, filter);
    return 0;
}



Là, logiquement je me dis "Youpi les knakis¸ ça va marcher !". Mais si je reteste, toujours rien. Rien ne s'affiche. La blague est même que si j'invoque :

$ sudo ./cap "not port 80"



... et que je joue du traffic http (sur le port 80 bien sûr), mon outil logue qu'il a vu un paquet ! Par contre avec "port 80" il n'affiche rien. Bon je me suis pas mal gratté la tête ici, j'avoue. Du coup j'appelle mon copain strace à la rescousse, et je compare le raw des filtres passés à setsockopt() entre mon code et l'appel tcpdump :

$ sudo strace -s 256 -f -v ./cap "port 80" 2>&1 | grep ATTACH_FILTER
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\33\0\0\0\0\0\0\0\20\240\363\0\0\0\0\0", 16) = 0
^C
$ sudo strace -s 256 -f -v tcpdump -ni dummy0 port 80 2>&1 | grep ATTACH_FILTER
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\1\0\0\0\0\0\0\0h\303\315E(\177\0\0", 16) = 0
setsockopt(3, SOL_SOCKET, SO_ATTACH_FILTER, "\30\0\0\0\0\0\0\0\360V1G(\177\0\0", 16) = 0
^C



Les outputs ne sont pas les mêmes. La génération du filtre est donc foireuse, je vérifie maintenant comme suit :

$ sudo tcpdump -nd port 80
(000) ldh      [12]
(001) jeq      #0x86dd          jt 2    jf 10
(002) ldb      [20]
(003) jeq      #0x84            jt 6    jf 4
(004) jeq      #0x6             jt 6    jf 5
(005) jeq      #0x11            jt 6    jf 23
(006) ldh      [54]
(007) jeq      #0x50            jt 22   jf 8
(008) ldh      [56]
(009) jeq      #0x50            jt 22   jf 23
(010) jeq      #0x800           jt 11   jf 23
(011) ldb      [23]
(012) jeq      #0x84            jt 15   jf 13
(013) jeq      #0x6             jt 15   jf 14
(014) jeq      #0x11            jt 15   jf 23
(015) ldh      [20]
(016) jset     #0x1fff          jt 23   jf 17
(017) ldxb     4*([14]&0xf)
(018) ldh      [x + 14]
(019) jeq      #0x50            jt 22   jf 20
(020) ldh      [x + 16]
(021) jeq      #0x50            jt 22   jf 23
(022) ret      #96
(023) ret      #0



Et j'invoque

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_RAW, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }
    bpf_dump(&bpf, 0);



J'ai en sortie :

Initializing interface dummy0 (filter 'port 80')
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
(000) ldb      [0]
(001) and      #0xf0
(002) jeq      #0x60            jt 3    jf 11
(003) ldb      [6]
(004) jeq      #0x84            jt 7    jf 5
(005) jeq      #0x6             jt 7    jf 6
(006) jeq      #0x11            jt 7    jf 26
(007) ldh      [40]
(008) jeq      #0x50            jt 25   jf 9
(009) ldh      [42]
(010) jeq      #0x50            jt 25   jf 26
(011) ldb      [0]
(012) and      #0xf0
(013) jeq      #0x40            jt 14   jf 26
(014) ldb      [9]
(015) jeq      #0x84            jt 18   jf 16
(016) jeq      #0x6             jt 18   jf 17
(017) jeq      #0x11            jt 18   jf 26
(018) ldh      [6]
(019) jset     #0x1fff          jt 26   jf 20
(020) ldxb     4*([0]&0xf)
(021) ldh      [x + 0]
(022) jeq      #0x50            jt 25   jf 23
(023) ldh      [x + 2]
(024) jeq      #0x50            jt 25   jf 26
(025) ret      #65536
(026) ret      #0
[dummy0] fix_code worked (rcode = 1) for filter 'port 80'
[dummy0] filter 'port 80' successfully installed
^C



On voit bien que c'est différent au début. Le reste est plutôt cohérent

(016) jeq      #0x6             jt 18   jf 17  # 0x06 c'est l'IPPROTO_TCP (6)
(017) jeq      #0x11            jt 18   jf 26  # 0x11 = 17 = IPPROTO_UDP



Si on n'est dans aucun des deux cas, on saute en 26 (jf 26), soit la fin. Sinon, on regarde si le port vaut 80 (0x50) :

(022) jeq      #0x50            jt 25   jf 23
(023) ldh      [x + 2]
(024) jeq      #0x50            jt 25   jf 26



Le problème est vraiment le prologue. Et là je on me souffle à l'oreille que c'est peut-être le datalink type. Réaction: "bon dieu de !#@@!#@!# mais c'est bien sûr, ça doit être mon datalink (DLT) qui est moisi !" Si j'écris :

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, DLT_IEEE802, &bpf, filter, 1, 0)) {



J'obtiens en sortie :

$ cc -o cap cap.c -lpcap && sudo ./cap "port 80" 
Initializing interface dummy0 (filter 'port 80')
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
(000) ldh      [20]
(001) jeq      #0x86dd          jt 2    jf 10  # 0x86dd est l'ethertype IPv6
(002) ldb      [28]
(003) jeq      #0x84            jt 6    jf 4
(004) jeq      #0x6             jt 6    jf 5
(005) jeq      #0x11            jt 6    jf 23
(006) ldh      [62]
(007) jeq      #0x50            jt 22   jf 8
(008) ldh      [64]
(009) jeq      #0x50            jt 22   jf 23
(010) jeq      #0x800           jt 11   jf 23  # 0x800 est l'ethertype IPv4
[...]



Mais toujours rien n'est logué si je joue du http ! En remplaçant DLT_IEEE802 par DLT_EN10MB en revanche...

[...]
[dummy0] enter in promiscuous mode
[dummy0] bind succeed: index 1634 <--> dummy0
[dummy0] fix_code worked (rcode = 1) for filter 'port 80'
[dummy0] filter 'port 80' successfully installed
[1286457559,615193] packet received



Hallelujah ! Hosanna hosanna !

Enfin bon, c'est quand même moche d'écrire le DLT en dur dans le code, donc je l'ai récupéré via la libpcap (mais on est obligé de passer par un handler pcap_t *, ce que je ne voulais pas trop au début...)

    char errbuf[256] = "";
    pcap_t *p = pcap_open_live(device, FRAME_SIZE, 1, 0, errbuf);
    if (! p) {
        printf("[%s] pcap_open_live failed: %s\n", device, errbuf);
        return -1;
    }

    int dlt = pcap_datalink(p);
    pcap_close(p);

    if (-1 == pcap_compile_nopcap(FRAME_SIZE, dlt, &bpf, filter, 1, 0)) {
        printf("[%s] pcap_compile_nopcap failed for '%s'\n", device, filter);
        return -1;
    }



En fait, comme on dispose du handler pcap_t, on peut même dégager pcap_compile_nopcap() et utiliser pcap_compile(), qui lui n'a même pas besoin d'un DLT en paramètre... Tout ça parce que j'ai voulu me passer de ce foutu handler, en somme. Bref le code est disponible ici

mercredi 15 septembre 2010

GCC ifunc!

Pendant ma petite pause lecture du matin, je suis tombé sur ce post concernant les nouvelles updates de la GNU toolchain, et en particulier un nouvel attribut: "ifunc"

Ca permet de définir dynamiquement une fonction qui va "résoudre" vers la fonction de votre choix en fonction de critères que vous pouvez définir, le monsieur @ http://nickclifton.livejournal.com/6612.html donne un exemple très simple et très clair (plus clair que mes explications foireuses) que je reproduirais ici :

void *slow_memcpy (void *dst, const void *src, size_t len)
{
         char *d = dst; char *s = src;

         while (len--)
           *d++ = *s++;

         return dst;
}

void *fast_memcpy (void *dst, const void *src, size_t len)
{
         __asm("foo %0 %1 %2" : "=m" (dst) :  "m" (src), "r" (len) : memory);
         return dst;
}

static void (* resolve_memcpy (void)) (void)
{
         return __cpu_has_foo () ? fast_memcpy : slow_memcpy;
}

void *memcpy (void *, const void *, size_t) __attribute__ ((ifunc ("resolve_memcpy")));

Les details chez http://nickclifton.livejournal.com/6612.html

lundi 12 avril 2010

C'est dans les vieux pots...

Suite à la mise à disponibilité de RE2, je suis tombé sur un article de Russ Cox qui explique en quoi l'algorithme de matching de Ken Thompson (Thompson NFA, pour Non Deterministic Algorithm) poutrait grave sa mère pour la reconnaissance de certaines grammaires (face à PCRE ou Perl), avec moins de 500 lignes de C. C'est intéressant. On a même droit à une implémentation (assez basique) en C. Yummy.

o l'article

vendredi 6 novembre 2009

meme vieux on continue d'apprendre

Je rentre dans ma 65 eme annee et je continue encore d'apprendre de nouvelles choses.. Au cours d'une discussion avec un pote sur "l'intrawebz" (tm) j'ai appris l'existence des register_printf_functions.. comme decrit dans les liens suivants:

Et au passage qqqes docs sur les attributes GCC et des ptits examples:

Edit de poz:

  • on note au passage que dans le premier lien, l'auteur ne sait pas comment virer un warning gênant à la compilation (dû à -Wformat, lui-même inclu dans -Wall) ; pour s'en débarrasser, il suffit de passer l'option -Wno-format à gcc. Ceci dit, il faut faire attention, parce qu'il ne râlera plus quand vous utiliserez le mauvais /specifier/ (genre %s au lieu de %zu), donc méfiance...
  • bon alors bien sûr c'est quand on migre son code que la merde arrive par tombereaux entiers :). Alors, register_printf_function() est deprecated à partir de glibc 2.10, il faut donc utiliser register_printf_specifier() à la place. Un coup de #if __GLIBC_PREREQ(2,10) sous linux devrait faire l'affaire... je mets au conditionnel parce que chez moi le printf() segfault :)

mercredi 30 septembre 2009

TCP/IP for dummies

Je ne sais pas pour vous, mais le réseau, je trouve ça assez intéressant. Comment fonctionnent les protocoles, leurs implémentations, etc. Alors voici un petit lien, présentant une implémentation de TCP/IP très légère, mais assez intéressante d'un point de vue didactique. Il s'agit de lwIP (light-weight IP), une stack TCP/IP ne faisant pas de copie de buffer afin de ne pas perdre trop en performances (mais avec une abstraction manquante, car chaque layer peut taper dans la couche sous-jacente), et où les principaux comportements attendus sont implémentés : silly window avoidance, demultiplexage, contrôle de checksum, retransmission rapide, calcul de RTT, contrôle de congestion, etc. La pile propose même une API BSD compliant :) Bon, il s'agit d'un PoC d'universitaire, hein, c'est pas ça qui va remplacer les piles des OS, mais encore une fois, ça se comprend vite et bien.

Ah, oui, le code (C, oeuf corse) tient en moins de 2600 lignes, et le code objet produit pèse 13.5 KiB.

PS : Dernière chose, il n'y a pas de support IPv6. Haaan, la grosse honte.

mercredi 26 août 2009

Des états de branche dans ta couverture GCC

Ce billet:

  • parle en bien de sqlite
  • explique la différence entre un programme sure et fiable
  • digère la page d'explication de test de sqlite, a lire pour les passionés de TDD
  • explique les branches et les états de ton code et comment utiliser GCC pour évaluer le taux de couverture

Bref une bonne petite leçon d'analyse de code pour testage efficace.

Source

mercredi 12 août 2009

Une petite piqûre d'hér... de rappel.

Bon ben je sais pas pour vous, mais j'ai toujours fait des trucs moches genre :


void
sighandler_foo(int nsig)
{
   printf("Signal %d caught.  Exiting.\n", nsig);
   exit(EXIT_SUCCESS);
}

J'ai schématisé, mais l'idée est là. Bah en fait faut surtout pas écrire cette horreur. Là, normalement vous me pointez du doigt en riant très fort, et je deviens rouge de honte. Mais j'ai vaincu ma peur du ridicule, et je le clame : oui ! je suis une bille !

Sinon, pour ceux qui ne savent pas, en fait j'ai appris ça en lisant un post de Solar Designer sur nmap-dev, qui expliquait par le menu pourquoi c'est super crappy :

Après avoir vérifié, rien de tout ça n'est écrit dans signal(2) sur ma machine (Linux), mais c'est très clair dans le man OpenBSD, en revanche.

HTH.

- page 1 de 3