Il y a quelques temps, j'ai du pondre un moyen d'utiliser deux connexions ADSL en sortie, sur un meme routeur. Alors oui, Linux peut utiliser plusieurs tables de routage, et c'est possible de rediriger des flux sur la bonne table en fonctions de criteres exploitables par netfilter/iptables. Sauf que, dans le cas d'une sortie en MASQUERADE, seule la premiere table est utilisee, joie.

Donc le petit daemon suivant (certifie ISO-1664) utilise la libipq (il faudrait peut etre le porter sur NFQUEUE) pour recuperer la destination des paquets, et l'ajouter au hasard sur l'une de gateway passees en argument, si c'est la premiere fois que cette destination est jointe.

A la fin d'execution, les routes inscrites sont supprimees.

Dans le cas ou une connexion tombe, les entrees ajoutees dans la table de routage sont supprimees automatiquement par le kernel. Il manque juste ici un signal handler pour gerer le “flush” des routes inscrites dans le g_tree correspondant a la gateway tombee.

Compiler avec:

gcc ipqrouted.c $(pkg-config --cflags glib-2.0) -I/usr/include $(pkg-config --libs glib-2.0) -lipq -o routed

Licence GNU GPLv3+

/*
 * ipqrouted, a simple route load balancer based on libipq
 * Copyright (C) 2007, Nicolas "fosco" Trecourt
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 3 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Nicolas "fosco" Trecourt <fosco@fosconetwork.org>
 */

Quelques includes :

#include <netinet/in.h>
#include <linux/netfilter.h>
#include <libipq.h>
#include <asm/types.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <net/route.h> /* really broken */
#include <sys/ioctl.h>
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <resolv.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <glib-2.0/glib.h>
#include <signal.h>

Suivi de quelques constantes habituelles :

#define E_SOCK 7
#define BUFSIZE 2048

Et de quelques declarations :

struct gateway
{
	char * IP;
	char * interface;
};
 
GCompareFunc compare (gconstpointer a, gconstpointer b) {
	return (GCompareFunc) strcmp((gchar*)a,(gchar*)b);
}
 
struct gateway gws[255];
GTree * routes;
int gwnb;
 
char * getgwif(char * gw) {
	int k;
	for (k = 0; k < gwnb; k++) {
		if ( ! strcmp(gws[k].IP, gw) ) {
			return gws[k].interface;
		}
	}
	return NULL;
}
 
static void die(struct ipq_handle *h)
{
	ipq_perror("passer");
	ipq_destroy_handle(h);
	exit(1);
}

Pour l'ajout d'une route :

int add_route(char * host) {
	static int skfd = -1;
	struct rtentry rt;
	struct in_addr in;
 
	char * ret=malloc(255);
	int pif, k;
 
	ret=g_tree_lookup(routes, host);
	if (ret == NULL) {
 
		for (k = 0; k < gwnb; k++) {
			if ( ! strcmp (gws[k].IP, host) ) {
				printf ("Not adding route to a gateway\n");
				return 0;
			}
		}
 
		pif = (int) (gwnb * (rand() / (RAND_MAX + 1.0)));
 
		inet_aton(host, &in);
		((struct sockaddr_in *) &rt.rt_dst)->sin_family=AF_INET;
		((struct sockaddr_in *) &rt.rt_dst)->sin_addr=in;
 
		inet_aton(gws[pif].IP, &in);
		((struct sockaddr_in *) &rt.rt_gateway)->sin_family=AF_INET;
		((struct sockaddr_in *) &rt.rt_gateway)->sin_addr=in;
 
		inet_aton("255.255.255.255", &in);
		((struct sockaddr_in *) &rt.rt_genmask)->sin_family=AF_INET;
		((struct sockaddr_in *) &rt.rt_genmask)->sin_addr=in;
 
		rt.rt_metric=1;
		rt.rt_flags = (RTF_UP | RTF_HOST | RTF_GATEWAY);
		rt.rt_dev=gws[pif].interface;
 
 
		if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
			perror("socket");
			return (E_SOCK);
		}
 
		if (ioctl(skfd, SIOCADDRT, &rt) < 0) {
			perror("SIOCADDRT");
			close(skfd);
			return (E_SOCK);
		}
 
		/* Close the socket. */
		(void) close(skfd);
		g_tree_insert(routes, host, gws[pif].IP);
		return (0);
	}
}

Et la suppression :

int del_route(char* host, char* gw) {
	static int skfd = -1;
	struct rtentry rt;
	struct in_addr in;
 
	inet_aton(host, &in);
	((struct sockaddr_in *) &rt.rt_dst)->sin_family=AF_INET;
	((struct sockaddr_in *) &rt.rt_dst)->sin_addr=in;
 
	inet_aton(gw, &in);
	((struct sockaddr_in *) &rt.rt_gateway)->sin_family=AF_INET;
	((struct sockaddr_in *) &rt.rt_gateway)->sin_addr=in;
 
	inet_aton("255.255.255.255", &in);
	((struct sockaddr_in *) &rt.rt_genmask)->sin_family=AF_INET;
	((struct sockaddr_in *) &rt.rt_genmask)->sin_addr=in;
 
	rt.rt_metric=1;
	rt.rt_flags = (RTF_UP | RTF_HOST | RTF_GATEWAY);
	rt.rt_dev=getgwif(gw);
 
	if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
		perror("socket");
		return (E_SOCK);
	}
	if (ioctl(skfd, SIOCDELRT, &rt) < 0) {
		perror("SIOCDELRT");
		close(skfd);
		return (E_SOCK);
	}
	/* Close the socket. */
	(void) close(skfd);
	return (0);
}

Pour le nettoyage :

GTraverseFunc cleanroutes (gpointer key, gpointer value, gpointer data) {
	del_route(key, value);
	return FALSE;
}
 
void clean() {
	printf("cleaning up routes\n");
	g_tree_traverse (routes, (GTraverseFunc) &cleanroutes, G_IN_ORDER, NULL);
	exit (0);
}

La boucle principale

int main(int argc, char **argv)
{
	int status;
	unsigned char buf[BUFSIZE];
	struct ipq_handle *h;
	int k;
 
	if (argc < 5) {
		printf("need at least 1 gw with interface\n");
		exit (0);
	}
 
	for(k = 1; k < argc; k++) {
		gws[(k-1)/2].IP=argv[k];
		k++;
		gws[(k-2)/2].interface=argv[k];
	}
	gwnb=k/2;
 
	printf("GWS: %d\n", gwnb);
	for (k = 0; k < gwnb; k++) {
		printf("- %s on %s\n", gws[k].IP, gws[k].interface);
	}
 
	(void) signal(SIGINT,clean);
	(void) signal(SIGTERM,clean);
 
	routes = g_tree_new( (GCompareFunc) &compare);
 
	srand(time(NULL));
 
	h = ipq_create_handle(0, PF_INET);
	if (!h) die(h);
 
	status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
	if (status < 0) die(h);

On a dit ISO-1664 hein!

	daemon(0, 0);

Un paquet arrive …

	do{ 
		status = ipq_read(h, buf, BUFSIZE, 0);
		if (status < 0) die(h);
 
		switch (ipq_message_type(buf)) {
			case NLMSG_ERROR:
				fprintf(stderr, "Received error message %d\n", ipq_get_msgerr(buf));
				break;
 
			case IPQM_PACKET: {
				ipq_packet_msg_t *m = ipq_get_packet(buf);
 
				int k;

… on recupere l'IP …

				char* IP = malloc(255);
				snprintf(IP, 255, "%d.%d.%d.%d", m->payload[16], m->payload[17], m->payload[18], m->payload[19]);

… on demande l'ajout …

				add_route(IP);

… et on laisse passer le paquet

				status = ipq_set_verdict(h, m->packet_id, NF_ACCEPT, 0, NULL);
 
				if (status < 0) die(h);
				break;
			}
 
			default:
				fprintf(stderr, "Unknown message type!\n");
				break;
		}
	} while (1);
 
	ipq_destroy_handle(h);
	return 0;
}
c/lib/load_balancing_de_routes_via_libipqueue.txt · Last modified: 2010/01/12 13:29 (external edit)
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0