PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Netfilter liefert falsche Ports. Bitte Helfen.



slaYer977
09.08.06, 22:17
Hallo,

ich habe eine kleines Kernelmodul geschrieben, welches die Funtionalität einer Firewall hat. Jedoch habe ich noch ein großes Problem. Ich bekomme von Netfilter nicht die richtigen Portnummern angezeigt. Wenn ich also per tcph->source auf den Source-Port zugreife und auch eine Konvertierung durchführe so bekomme ich, wenn das Paket von Extern kommt nicht die richtigen Ports angezeigt. Wenn ich auf der selben Maschine z.b. wget localhost aufrufe, werden die richtigen Ports angezeigt. Wobei dann auch noch zwei Pakete angezeigt werden, wo ich nicht verstehe, warum diese auch immer mitgeneriert werden.

Ich habe zunächst vermutet, dass Problem an meinem Fedora 4 System liegt. Dass dort evtl. irgendwelche Security Features (SELinux) dass ganze verursachen. Nun habe ich aber ein Ubuntu 6.06 auf einem Testsystem installiert und da habe ich das gleiche Problem. Als nächstes würde ich es mit Red Hat 9 probieren, doch da muss ich zuerst noch den Kernel neu kompilieren, damit ich Module per insmod laden kann.

Wäre nett, wenn mir einer sagen könnte, warum ich die falschen Portnummern zu Gesicht bekomme. Interessant ist auch, dass wenn ich von Extern komme es scheinbar keine Geige spiel, welchen Port ich anspreche (z.B. per Webbrowser 192.1.1.1:1234) Mir wird immer der selbe falsche Port angezeigt.

Hier zuerst mal die Ausgabe von der Konsole:


wget localhost
Source: 127.0.0.1 : 46670 Destination: 127.0.0.1 : 80
Source: 127.0.0.1 : 17664 Destination: 127.0.0.1 : 44 <--verstehe ich nicht
Source: 127.0.0.1 : 80 Destination: 127.0.0.1 : 46670
Source: 127.0.0.1 : 17664 Destination: 127.0.0.1 : 40 <--verstehe ich nicht

von extern per http://85.176.149.21:irgendeinport (meine öffentliche ip-adresse zu diesem zeitpunkt)
Source: 85.176.157.129 : 17664 Destination: 85.176.149.21 : 48




Ich habe mein Kernelmodul für die Fehleranalyse mal auf das Wesentliche reduziert:
(Da dass wget localhost ja scheinbar funktioniert, denke ich dass meine convert_port-Methode auch das richtige tut.)



//portprob.c
#include <linux/netfilter_ipv4.h>
#include </usr/src/kernels/2.6.11-1.1369_FC4-i686/include/net/icmp.h>




static struct nf_hook_ops in; /*NF_IP_PRE_ROUTING - Hook 1 */
static struct nf_hook_ops out; /*NF_IP_POST_ROUTING - Hook 4 */

//Array-Structure für eine IP-Adresse
struct addrarray{ unsigned char ipbyte[4]; };

/*Diese Funktion konvertiert eine IP-Adresse, die von netfilter als eine
* grosse Zahl im u32-Format übergeben wird, in vier eigenständige Bytes.
* Das Makro NIPUQAD wurde bewusst nicht verwendet.
*/
struct addrarray convert_ip(unsigned int ipaddress)
{
unsigned char rueckgabe;
unsigned int tmp;
unsigned int einbit = 0x01;
unsigned int *zeiger1;
struct addrarray addr;

int i;
int j;
int k;

//Zeiger zeigt auf die u32-ipadresse
zeiger1 = &ipaddress;




//for-schleife wird vier mal durchlaufen, da wir 4 Bytes haben wollen
for(j=1;j<=4;j++)
{
i=8*j-1;
k=i-7;
tmp=0;


//schleife wird 8mal durchlafuen. für jedes bit einmal.
for(; i>=k;i--)
{
einbit = 0x01;
einbit = (einbit << i);

if ((*zeiger1 & einbit)){tmp=tmp^einbit;};
}

rueckgabe=tmp>>k;
addr.ipbyte[j-1]=rueckgabe;
}
return addr;
}



/*Konvertierung der portnummer in das richtige format.
*/
unsigned int convert_port(unsigned int port)
{
unsigned int rueckgabe;
unsigned int einbit=0x01;
unsigned int *zeiger;
unsigned int tmp;
int j=1;
int i;
int k;

zeiger=&port;


rueckgabe=0;

for(;j<=2;j++)
{
i=8*j-1;
k=i-7;
tmp=0;

for(; i>=k;i--)
{
einbit = 0x01;
einbit = (einbit << i);

if ((*zeiger & einbit)){tmp=tmp^einbit;};
}

if(j==1){tmp<<=8;};
if(j==2){tmp>>=8;};
rueckgabe=rueckgabe^tmp;
}

return rueckgabe;
}






//MAIN FUNCTION
unsigned int main_hook(unsigned int hooknum,
struct sk_buff **skb_p,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff*))
{

struct sk_buff *skb = *skb_p;
struct iphdr *iph = skb->nh.iph;
struct tcphdr *tcph = skb->h.th;
struct udphdr *udph = skb->h.uh;

struct addrarray saddr;
struct addrarray daddr;

unsigned int sport = 0;
unsigned int dport = 0;

//some conversions
saddr = convert_ip(iph->saddr);
daddr = convert_ip(iph->daddr);


if(iph->protocol == 6)
{
sport = convert_port(tcph->source);
dport = convert_port(tcph->dest);
}

if(iph->protocol == 17)
{
sport = convert_port(udph->source);
dport = convert_port(udph->dest);
}


if((iph->protocol == 6) || (iph->protocol == 17))
{

printk("Source: %u.%u.%u.%u : %u Destination: %u.%u.%u.%u : %u \n" ,saddr.ipbyte[0],saddr.ipbyte[1],saddr.ipbyte[2],saddr.ipbyte[3], sport, daddr.ipbyte[0],daddr.ipbyte[1],daddr.ipbyte[2],daddr.ipbyte[3],
dport);
}


return 1;
}


//INIT Methode zum Laden des Moduls in den Kernel
int init_module()
{

printk("\n Modul has been loaded...\n");

in.hook = main_hook;
in.pf = PF_INET;
in.hooknum = NF_IP_PRE_ROUTING;
in.priority = NF_IP_PRI_FIRST;

out.hook = main_hook;
out.pf = PF_INET;
out.hooknum = NF_IP_POST_ROUTING;
out.priority = NF_IP_PRI_FIRST;

nf_register_hook(&in); /*register NF_IP_PRE_ROUTING hook*/
nf_register_hook(&out); /*register NF_IP_POST_ROUTING hook*/


return 0;
}



//CLEANUP zum entladen des Moduls aus dem Kernel
void cleanup_module()
{
printk("\n Modul has been unloaded...\n");

nf_unregister_hook(&in); /*unregister Module from Hook 1*/
nf_unregister_hook(&out); /*unregister Module from Hook 2*/
}




Bin für jede Hilfe sehr dankbar!

derRichard
09.08.06, 22:30
hi!

ok, hab den thread doch selber gefunden. ;)
warum konvertierst du den port nicht mit ntohs()?

zb:
printk("sender: %u.%u.%u.%u, port: %u\n", NIPQUAD(iph->saddr), ntohs(tcph->dest));

//richard

slaYer977
09.08.06, 22:37
nee, dass soll bewusst nicht gemacht werden. Bei dieser Firewall soll so viel wie möglich selbst programmiert werden. Habe auch bei der Konvertierung von der IP-Adresse auf NIPQUAD bewusst verzichtet.

derRichard
09.08.06, 22:40
hallo!

ja, aber du hast sicher einen bug in deiner port-umrechungs-funktion. (den ich jetzt sicher nicht für dich suchen werde ;) )
und wenn die funktionen vom kernel nichtmal verwenden werden dürfen, dann ist dann einfach nur dämlich.

//richard

slaYer977
09.08.06, 22:51
Schon mal gut zu wissen, dass es auch für die Port-Umwandlung ein Makro gibt. Ich werde es mir morgen mal anschauen. Hoffentlich steige ich da durch und verstehe was es macht. Werde dann versuchen meien Methode dementsprechen anzupassen.

Gibt es eigentlich auch für ICMP-Type und ICMP-Code auch Makros? Da glaube ich habe ich das gleiche Problem.

Auf jeden Fall Vielen Dank.

--> So ich gehe jetzt ins Bett ;-)

slaYer977
10.08.06, 19:42
Shit, was mache ich jetzt?

Ich habe jetzt mal das ntohs-Makro mit eingebaut und die Ports sind die selben, wie bei meiner eigenen Umrechnung. Habe von eine zweiten internen PC per Browser also Port 80 auf die Linuxfirewall zugegriffen. Aber man sieht nach wie vor keinen Zugriff auf Port 80. Nur eine Antwort von diesem Port.

Es sieht immer nur so aus, was auf der Konsole ankommt:
(Die Zeile die mit Richtig anfängt verwendet das Makro)


Aug 10 20:41:15 slayer977 kernel: Source: 192.168.198.97 : 17664 Destination: 192.168.198.1 : 48
Aug 10 20:41:15 slayer977 kernel: Richtig: Source: 192.168.198.97 : 17664 Destination: 192.168.198.1 : 48
Aug 10 20:41:15 slayer977 kernel: Source: 192.168.198.1 : 80 Destination: 192.168.198.97 : 1047
Aug 10 20:41:15 slayer977 kernel: Richtig: Source: 192.168.198.1 : 80 Destination: 192.168.198.97 : 1047
Aug 10 20:41:16 slayer977 kernel: Source: 192.168.198.97 : 17664 Destination: 192.168.198.1 : 48
Aug 10 20:41:16 slayer977 kernel: Richtig: Source: 192.168.198.97 : 17664 Destination: 192.168.198.1 : 48
Aug 10 20:41:16 slayer977 kernel: Source: 192.168.198.1 : 80 Destination: 192.168.198.97 : 1047
Aug 10 20:41:16 slayer977 kernel: Richtig: Source: 192.168.198.1 : 80 Destination: 192.168.198.97 : 1047






Am Quelltext habe nich nur dies geändert:
(nur ein zweite printk-Anweisung, wo statt meiner Umrechnug das ntohs-Makro verwendet wird.)


if((iph->protocol == 6))
{

printk("Source: %u.%u.%u.%u : %u Destination: %u.%u.%u.%u : %u \n" ,saddr.ipbyte[0],saddr.ipbyte[1],saddr.ipbyte[2],saddr.ipbyte[3], sport, daddr.ipbyte[0],daddr.ipbyte[1],daddr.ipbyte[2],daddr.ipbyte[3],
dport);

printk("Richtig: Source: %u.%u.%u.%u : %u Destination: %u.%u.%u.%u : %u \n" ,saddr.ipbyte[0],saddr.ipbyte[1],saddr.ipbyte[2],saddr.ipbyte[3], ntohs(tcph->source), daddr.ipbyte[0],daddr.ipbyte[1],daddr.ipbyte[2],daddr.ipbyte[3],
ntohs(tcph->dest));

}

slaYer977
10.08.06, 21:26
Habe eine Lösung gefunden! :-)

Also meine Umwandlung BigEndian<->LittleEndian ist soweit ganz richtig. Es liegt nicht daran. Habe auf einer Webseite ein Code-Schnipsel gefunden wo der Port richtig ausgelesen wird. Ich habe das jetzt in mein Code integriert und siehe da es funzt!

Die Deklaration von thead ist hier das wichtige!
Und meine convert_port Methode wird einfach auf thead verwendet. Man könnte auch tohs() nehmen.



struct tcphdr *thead;
thead = (struct tcphdr *)(skb->data +(skb->nh.iph->ihl * 4));

sport = convert_port(thead->source);
dport = convert_port(thead->dest);



Jetzt bekomme ich endlich die richtigen Ports.
Nur leider verstehe ich die Zeile mit der Deklaration nicht. Scheinbar scheint in skb->data relevante Informationen bzgl. des Ports versteckt zu sein. Und warum muss ich den ihl * 4 nehmen???

Auf dieser Webseite bin ich darüber gestoßen:
http://www.topsight.net/article.php?story=2003050621055083&mode=print#sec2_2

derRichard
10.08.06, 21:57
hallo!

das ist ja die selbe deklaration, wie ich sie auch in dem beispielcode von mir verwendet habe, den ich dir vor längerer zeit mal gepostet habe.

du musst mal den ihl mit 4 multiplizieren weil, der ihl die länge in 32 bit-wörtern angibt. ein wort hat bei uns aber 8 bit.
32/8 = 4.

zugegeben es ist a bissl kompiliziert.^^

//richard