Einige Fragen bzgl. Softwareentwicklung...

D

das.chaos

Guest
Nun denn, ich hatte mich wohl (zuvor) ins falsche Unterforum verirrt und die falschen Fragen gestellt (sowas passiert mir abundan, wenn ich dann irgendwie mich verhaspel - liegt am kaffee - und vom Thema abschweife, mein Nickname kommt nicht von ungefaehr), egal... sorry, habe wenig Erfahrung mit www Foren und dergleichen, da ich mich eher selten im sogenannten www des Internets bewege.

Im Laufe der Zeit, seitdem ich FreeBSD kennenlernte, entwickelte sich bei mir (zunaechst aus reiner Neugier) ein signifikantes Interesse bzgl. Betriebssystemarchitektur und dessen programmatischer Optimierung.

Bevor man als Student eigeninitiativ (in Bibliotheken und oder im besagten www, wie oftmals von der Systemumwelt gefordert wird), vernuenftig (!) recherchieren kann, ist es manchmal als "Unwissender" unumgaenglich, Wissende nach bestimmten Namentlichen Bezeichnungen oder Begrifflichkeiten aus dem Bereich Betriebsystementwicklung und Softwarearchitektur mit der Zielsetzung zu erfragen (ohne dasz man als sogenannter Kretin :) bzw. unwissentlich als Provokant wahrgenommen wird), um Recherecheaktivitaeten in eine guenstige Richtung zu verlagern. Eine detaillierte Recherche gestaltet sich schwierig, wenn man nach was sucht, was man nicht namentlich kennt (oder kennen kann).

Um es vorweg zu nehmen, Literaturverweise die innerhalb folgenden Quellen existieren:
o Design and Implementation of the FreeBSD Operating System, McKusick
o TCP/IP Illustrated Vol. 2, Stevens
o Buecher von Joseph Kong
o Modern Operating Systems, Tannenbaum
habe ich schon beruecksichtigt und bin diese am analysieren. Unter anderem beruecksichtige ich bspw. in /usr/share/doc und /usr/share/examples existierende Dokumentation.

Nun, einige ernstgemeinte Fragen...

#1:

Fuer Testzwecke habe ich mir mal just for fun eine netgraph(4) Klasse implementiert, welche einen OSI-L2 basierenden Datenstrom nach Rahmentyp demultiplexiert und umgekehrt (man musz ja mit seinem Computer etwas "spielen" und sich "austoben" bzgl. Autodidaktik).

Das Verhalten einer Instanz steht in Abhaengigkeit, welche Hooks in Bezug zur Systemumwelt dieses Knotens stehen bzw. ob diese einen Hook einer Partnerinstanz referenzieren. D. h. keine Referenz impliziert keinen moeglichen Datenkanal fuer systeminterne Verteilung von Information (bzw. Referenzen auf Dateneinheiten /-objekte). Mindestens der Downstreamhook und ein beliebig anderer Hook sollten mit fremden Instanzen (oder auch der selben und mit mindestenz einer fremden Instanz, die mindestens einen Hook oder mehrere Hooks zur Verfuegung stellen kann) assoziiert sein, sonst werden keine message_primitives (OSI-Terminologie: gemeint sind Mbufs oder auch diese kapselnden struct ng_item Container) verarbeitet (entweder werden gebundene Ressourcen per m_freem(9) freigegeben sowie die der erwaehnten Container oder wiederum das Einbetten von Mbufchains oder Mbufs in einen besagten Container und Uebergabe seiner Referenz an eine Partnerinstanz).

Ich bin mal so mutig und poste (als unerfahrene Person bzgl. C im Kontext von Systementwicklung, daher bitte nicht Steinigen wenn Fehler gegeben sind, aufgrund mir unbekannter Ur- und Tatsachen, ein Malerlehrling kann unmoeglich ein Picasso sein) einen Auszug, um den Kontext der hier gegebenen Fragestellung (#1) darzustellen. Nun gut, dann sorge ich mal fuer *more drama* :)
Code:
struct protoent {  
    uint16_t type;  /* frame type */
    hook_p hook;    /* refers peer hook of given instance */ 
};

struct ng_etype_split_softc {   
    struct protoent sc_arp;
    struct protoent sc_ip;
    struct protoent sc_pppoe;
    struct protoent sc_pppoedisc;
    struct protoent sc_revarp;
        
    struct protoent sc_orphan;
    struct protoent sc_downstream;

    ... unwichtiger trash, statistiken, etc.pp. ...

    node_p node; /* backpointer to node structure of instance */
};
typedef struct ng_etype_split_softc *sc_p;

 ... nochmehr trash, constructoren, destructoren, messaging stuff ...

static int
ng_etype_split_rcvdata(hook_p hook, item_p item)
{ 
	int err = 0;
	struct mbuf *m;
	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
	const struct protoent *src = NG_HOOK_PRIVATE(hook);
	
	if (!src) {
		err = ENETDOWN;
		if (item)
			NG_FREE_ITEM(item);
	} else { 	
		struct protoent *dst;
       	 	struct ether_header *eh;
        	uint16_t type;  
               
	        NGI_GET_M(item, m);
		if (m->m_pkthdr.len < ETHER_HDR_LEN) { 
			err = EINVAL;
			goto bad;
		}
	
		/* 
		 * If data field of message primitive  
		 * is contiguous. 
		 */
		if (m->m_len < ETHER_HDR_LEN) {
			m = m_pullup(m, ETHER_HDR_LEN);
			if (!m) {
				err = ENOBUFS;
				goto bad;
			} 
		}
	
        	eh = mtod(m, struct ether_header *);
        	type = ntohs(eh->ether_type);

		switch (src->type) {
		case ETHERTYPE_ARP:		
		case ETHERTYPE_IP:
		case ETHERTYPE_PPPOE:	
		case ETHERTYPE_PPPOEDISC:		
		case ETHERTYPE_REVARP:
			if ((src->type == type) && (sc->sc_downstream.hook)) 
                                NG_FWD_NEW_DATA(err, item, sc->sc_downstream.hook, m);
                        else {
				err = EINVAL;
				goto bad;
			}
                        
                        break;
		case ORPHAN:
			if (!sc->sc_downstream.hook) {
				err = EINVAL;
				goto bad;
			}
			
                        NG_FWD_NEW_DATA(err, item, sc->sc_downstream.hook, m); 
                        
			break;     
		default: /* If frame comes from downstream... */
			switch (type) {
			case ETHERTYPE_ARP:		
				dst = &sc->sc_arp;
				break;
			case ETHERTYPE_IP:
				dst = &sc->sc_ip;
				break;
			case ETHERTYPE_PPPOE:	
				dst = &sc->sc_pppoe;
				break;
			case ETHERTYPE_PPPOEDISC:		
				dst = &sc->sc_pppoedisc;
				break;
			case ETHERTYPE_REVARP:
				dst = &sc->sc_rarp;
				break;
			default:
				dst = &sc->sc_orphan;
			}

		/*
		 * ... every mbuf chain wich not holds data in a 
	         * cluster may be transformed for further processing,
                 * but single message primitives may passed and won't
		 * touched or passed throught orphan hook, if possible.
		 */
			if (dst->hook) {
				if (m->m_next) { 
					int len = m->m_pkthdr.len;
					char buf[len];
                    			struct mbuf *n;
					
					MGET(n, M_NOWAIT, MT_DATA);
					if (!n) {
						err = ENOBUFS;	
						goto bad;
					}
				
					MCLGET(n, M_NOWAIT);
					if (!(n->m_flags & M_EXT)) {
						m_freem(n);
						err = ENOBUFS; 
						goto bad;
					}
			
					m_move_pkthdr(n, m);
					m_copydata(m, 0, len, buf);
					m_freem(m);
					
					if (!m_append(n, len, buf)) {
						m_freem(n);
						err = ENOBUFS; 
						goto bad;
					}	
						
					NG_FWD_NEW_DATA(err, item, dst->hook, n);
				} else 
					NG_FWD_NEW_DATA(err, item, dst->hook, m);			
			} else {
				if (!sc->sc_orphan.hook) { 
					err = EINVAL;
					goto bad;
				}
                                
                                NG_FWD_NEW_DATA(err, item, sc->sc_orphan.hook, m); 
			}	
		}
	}
	
	return(err);
	
bad:	
	NG_FREE_M(m);
	if (item)
		NG_FREE_ITEM(item);
		
	return(err);
}

Im Hinblick gegebener Autodidaktik studiere ich (seit geraumer Zeit intensiv), im Rahmen des Informatikstudiums und aus privaten Interesse, den mir in /usr/src/sys dargebotenen Quellcode, mir ist aufgefallen, dasz haeufig von goto's bzgl. Exceptionhandling, was ich als Verfahrensweise naiv in o.g. Beispielcode uebertragen habe.

Es existiert stets die Moeglichkeit Exceptionhandling
Code:
MGET(n, M_NOWAIT, MT_DATA);
if (!n) {
    err = ENOBUFS;	
    goto bad;
}
bspw. als
Code:
MGET(n, M_NOWAIT, MT_DATA);
if (!n) {
    NG_FREE_M(m);
    NG_FREE_ITEM(item);
    return(ENOBUFS);	
}
zu realisieren.

Naiv gefragt: Gibt es spezifiche (technische) Vorraussetzungen (Stichworte: heap, stack, text, bss Segment ?!??), wann eine der beiden Formen von Exceptionhandling zu praeferieren sei und oder gemischte Verfahrensweisen geboten sind? Steht dieses im Bezug zur Effizienz (O(blablubb) etc.pp.)? Wieso werden goto's als schlechter Stil betrachtet, aber warum wird haeufig davon gebrauch gemacht, insbesonders dann, wenn es sich um Softwaremodule handelt, die dem Kernelspace zugeordnet sind?

#2:
Existieren, abgesehen von style(9), Literatur oder Industrielle Standards (Normschriften), welche Programmierrichtlinien bzgl. Kernel Normal Form (KNF) diskutieren? Gibt es eine zu empfehlen aus perspektive eines (erfahrenen) Systementwicklers?

#3:
Mir wurde MISRA-C empfohlen (*danke*), was ich mir umgehend bestellt habe. Gibt es nocht mehr derartige Richtlinien, die Unix Systementwicklung bzw. Betriebsystemarchitektur zum Gegenstand haben?

#4
Oder mir ist bei naeherer Untersuchung von Quellcode, bspw. aus /usr/src/net/if_clone.h, folgende Konvention bzgl. Strukturen aufgefallen
Code:
struct if_clone { 
    ... 
    const char * ifc_name; 
    int ifc_maxunit; 
    ... 
};
Bei Artefakten bzw. Members wird in der namentlichen Bezeichnung ein Praefix aus den ersten Buchstaben der Bestandteile der Strukturbezeichnung gebildet.
Code:
Code:
Strukturname: if_clone => Membername: ifc_blablubb
dann begegne ich beim Quellcodestudium oftmals Strukturen, wo diese Konvention nicht gelten vermag. Ist die o.g. Form namentlicher Bezeichnung willkuerlich oder hat dies einen speziellen Hintergrund bzw. eine spezifische Systematik, der irgendeiner (mir logischerweise unbekannten) Industrienorm zugrundeliegt? (Wenn ja, welche??? Wo kann ich diesbezueglich Informationen abschoepfen?). Usw.Usf.

Auf welchen Namensraumkonvetionen baut denn der Quellcode oder Teile aus /usr/src/sys auf???

Literaturtips?

#5:
Vor geraumer Zeit habe ich mir das Buch "Design Patterns" beschafft, gibt es aehnliche empfehlenswerte Literatur, welche sich aus Softwareentwicklersicht spezifisch mit Betriebssystemarchitektur befasst (ich meine jetzt nicht "Modern Operating Systems" von Tannenbaum - das ist mir zu oberflaechlich)?

Ich wuerde mich ueber fundierte Antworten freuen, da ich ein ernsthaftes Interesse verfolge, irgendwann einen klitzekleinen und unbedeutenden Beitrag zum FreeBSD Projekt leisten zu koennen (ich kann ja nicht nur nehmen, ich will auch zurueckgeben).
 
Zuletzt bearbeitet von einem Moderator:
Hi,

ich versuche Dir mal Anregungen zu geben, die meinem Erfahrungsraum entstammen. D.h. was ich schreibe ist gefärbt durch meine persönliche Situation, Erfahrung und Wahrnehmung und es kann durchaus andere, gute und gültige Meinungen geben die mir vllt. widersprechen.

Beim Code-Snippet in #1 hilft es mal kurz durchzudenken, was der C-Code in Assembly bedeutet. Das darfst Du selbst tun, denn dadurch werden Dir die Gemeinsamkeiten oder Unterschiede der beiden Varianten klar. Bitte beachte, dass sich speziell in der 2. Variante der von einem C++ Compiler erzeugte Code von dem eines C Compilers unterscheiden kann.

Hier scheint schon meine Grundüberzeugung durch: Es hilft einfach, zu verstehen was ein Compiler mit Quellcode und was später ein Prozessor mit Binärcode macht. Es lohnt meiner Meinung nach wirklich, den gesamten Pfad einmal durchzudenken.

Darum kann und will ich gar nicht im Detail auf die weiteren Fragen eingehen. Nur eines, da Du im Prinzip nach Coding Guidelines fragst: Schau Dir mal den Google Guide an (http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml). Dort werden auch Pro- u. Contra-Argumente gelistet, denn wie so oft gibt es keine goldene Antwort.
 
Ich wollte mir am Wochenende die Zeit nehmen mal auf Deine Fragen einzugehen.

Aber der Vorherige Beitrag sollte nicht unkommentiert bleiben:
Hier scheint schon meine Grundüberzeugung durch: Es hilft einfach, zu verstehen was ein Compiler mit Quellcode und was später ein Prozessor mit Binärcode macht. Es lohnt meiner Meinung nach wirklich, den gesamten Pfad einmal durchzudenken.

Damit hat er nämlich vollkommen und uneingeschränkt Recht. Das werde ich morgen hoffentlich noch etwas ausführen.
 
Hi,

ich bin gespannt auf Kamikazes Antwort und eure Antworten, werte Mitmenschen.

Nun, ich bin bestrebt an diesem Thread (ernsthaft) mitzarbeiten (ich will's verstehen, was die Maschine da macht), daher habe ich angefangen (wie Vincent Vega anregte) mich mit Assembler (AT&T Syntagma) auseinanderzusetzen. Der Codeschnipsel aus (#1) wurde etwas von mir umgeschrieben (die else. Verzweigung wurde neutralisiert), dann habe ich Assemblercode von beiden Varianten, welche sich bzgl. Exceptionhandling unterscheiden, erzeugt.

Momentan versuche ich die Assemblerinstruktionen zu verstehen (hochinteressant) und analysiere wie das Layout des durch die Netgraph Funktion dargestellten Stackframe strukturiert ist und welche Pfade (die durch bspw. jmpl, je, jne, j<x|y|z|...> implizierten Spruenge) und Teilsterecken innerhalb diesem System (gemeint ist die Funktion) vorkommen und welche moeglichen Konsequenzen sich, im Hinblick auf das Entwickeln von Software (bspw. bzgl. Komplexitaetsreduktion eines entwickelten Konzepts), ergeben.

Es sei angemerkt meinerseits (bei naiver Betrachtungsweise), dasz mich einige Symbole, bspw.
Code:
.L1, .L2, ... , Ln, \for all  i \in \mathbb{N_+},
welche "Pfade" (???) oder Abschnitte kennzeichnen, mich verdaechtig an das Konzept vorkommender Automatenzustaende einer FSM erinnern. Um es vorwegzunehmen, bei oberflaechlicher Betrachtung fallen weniger Instruktionen an, wenn ich die Ausnahmebehandlung ausschlieszlich mittels Sprunganweisungen realisiere. Aber irgendwie gefaellt mir (intuitiv) die andere Variante besser, dennoch bin ich (momentan) am ergruenden, inwiefern sich die unterschiedlichen Assemblerinstruktionen bzgl. (ihrer zugrundeliegenden) Komplexität charakterisieren, ferner inwiefern sich das Verhaeltnis von Gesamtanzahl aller Instruktionen zur Anzahl moeglicher Verzweigungen bzgl. Anzahl gegebener Instruktionen je Pfad (s_0 => s_final) und der der Teilstrecke gestaltet und ob dies moeglicherweise Rueckschluesse auf Alternativeimplementierungen zuliesze).
 
Zuletzt bearbeitet von einem Moderator:
...
#1:
Es existiert stets die Moeglichkeit Exceptionhandling
Code:
MGET(n, M_NOWAIT, MT_DATA);
if (!n) {
    err = ENOBUFS;	
    goto bad;
}
bspw. als
Code:
MGET(n, M_NOWAIT, MT_DATA);
if (!n) {
    NG_FREE_M(m);
    NG_FREE_ITEM(item);
    return(ENOBUFS);	
}
zu realisieren.

Naiv gefragt: Gibt es spezifiche (technische) Vorraussetzungen (Stichworte: heap, stack, text, bss Segment ?!??), wann eine der beiden Formen von Exceptionhandling zu praeferieren sei und oder gemischte Verfahrensweisen geboten sind? Steht dieses im Bezug zur Effizienz (O(blablubb) etc.pp.)? Wieso werden goto's als schlechter Stil betrachtet, aber warum wird haeufig davon gebrauch gemacht, insbesonders dann, wenn es sich um Softwaremodule handelt, die dem Kernelspace zugeordnet sind?

Beide Ansätze sind schlicht Workarounds für das Fehlen von Exception Handling. Goto ist zwar schlechter Stil, hat jedoch den Vorteil, dass weniger Code dupliziert wird (was Widerum guter Stil ist). Alternativ könnte man mit einem Makro das goto vermeiden. Das trägt natürlich nicht unbedingt zur Wartbarkeit bei. Alle Ansätze sind also in irgend einer Hinsicht schlechter Stil, einen Tod muss man sterben - welchen, darauf komme ich noch.

#2:
Existieren, abgesehen von style(9), Literatur oder Industrielle Standards (Normschriften), welche Programmierrichtlinien bzgl. Kernel Normal Form (KNF) diskutieren? Gibt es eine zu empfehlen aus perspektive eines (erfahrenen) Systementwicklers?
Es gibt style(9) was in meinen Augen ziemlich Nutzlos ist. Es wird sich in der Regel an dem Orientiert was schon da ist, gelegentlich ist hier und da mal ein Ego zu groß, dann gibt es Stil-Potpourri.

#3:
Mir wurde MISRA-C empfohlen (*danke*), was ich mir umgehend bestellt habe. Gibt es nocht mehr derartige Richtlinien, die Unix Systementwicklung bzw. Betriebsystemarchitektur zum Gegenstand haben?
Ich bin persönlich kein Fan von Misra. Das erzeugt viel Code der dazu da ist Flüchtigkeitsfehler und Inkompetenz systematisch zu verhindern. Das bläht den Code allerdings auf. Stattdessen könnten die Richtlinien den Einsatz statischer Analysetools verbindlich machen, da gibt es sehr gute, die eine Menge Fehler finden, die wahrscheinlich selbst Misra nicht vermeiden würde.

#4
Oder mir ist bei naeherer Untersuchung von Quellcode, bspw. aus /usr/src/net/if_clone.h, folgende Konvention bzgl. Strukturen aufgefallen
Code:
struct if_clone { 
    ... 
    const char * ifc_name; 
    int ifc_maxunit; 
    ... 
};
Bei Artefakten bzw. Members wird in der namentlichen Bezeichnung ein Praefix aus den ersten Buchstaben der Bestandteile der Strukturbezeichnung gebildet.
Code:
Code:
Strukturname: if_clone => Membername: ifc_blablubb
dann begegne ich beim Quellcodestudium oftmals Strukturen, wo diese Konvention nicht gelten vermag. Ist die o.g. Form namentlicher Bezeichnung willkuerlich oder hat dies einen speziellen Hintergrund bzw. eine spezifische Systematik, der irgendeiner (mir logischerweise unbekannten) Industrienorm zugrundeliegt? (Wenn ja, welche??? Wo kann ich diesbezueglich Informationen abschoepfen?). Usw.Usf.

Auf welchen Namensraumkonvetionen baut denn der Quellcode oder Teile aus /usr/src/sys auf???

Literaturtips?
Ich vermute hier spielt wieder das Thema persönliche Vorlieben eine Rolle. Wie schon gesagt hat FreeBSD in meinen Augen keinen hinreichenden Style Guide.

Es kann gut sein, dass die Structs in einem Kontext verwendet werden, in dem ein anderes Struct mit gleich benannten Members aufbaut. Wenn man jetzt davon ausgeht, dass viele Programmierer leider bescheuerte, nichtssagende Varabiablennamen verwenden (auch historisch gewachsen, früher galten nur die ersten 8 Zeichen eine Bezeichners) kann man sich vielleicht besser Vorstellen, wie man den Quatsch für eine gute Idee halten kann (erfundenes Beispiel):
Code:
i->ifc_maxunit = 1024;
f->frm_maxunit = 512;

#5:
Vor geraumer Zeit habe ich mir das Buch "Design Patterns" beschafft, gibt es aehnliche empfehlenswerte Literatur, welche sich aus Softwareentwicklersicht spezifisch mit Betriebssystemarchitektur befasst (ich meine jetzt nicht "Modern Operating Systems" von Tannenbaum - das ist mir zu oberflaechlich)?
The Design and Implementation of the FreeBSD Operating System genießt, obgleich veraltet, immer noch einen hervorragenden Ruf.

Jetzt kommen wir mal zu einem Ratschlag, den ich für Dich habe. Es ist sehr lobenswert, dass Du Dich so intensiv mit dem Thema befasst. Das ist auch notwendig um ein Verständnis dafür zu entwickeln, was um Dich herum passiert und zweifelsohne entwickelst Du damit wertvolle Kompetenzen.

Am Ende Deiner Reise kommst Du aber (in meinen Augen zwangsweise) zu folgender Erkenntnis. Jeder Stil hat vor- und Nachteile. Es gibt Ansätze, die sind definitiv schlecht aber auch viele die sind möglicherweise gut geeignet und keinen der definitiv gut geeignet ist. Deshalb wirst Du egal wo du hinkommst andere Vorgaben finden. Wenn du selbst die Fäden in der Hand hast, kannst Du den Stil diktieren. Aber die wichtigste Erkenntnis ist folgende: der Stil ist nicht sonderlich wichtig - was wichtig ist, ist dass ALLE DEN GLEICHEN verwenden.

Wenn also mit schlechten Vorgaben arbeitest gibt es drei Möglichkeiten:
1) Überzeuge die entsprechende Autorität die Vorgaben zu Ändern
2) Ignoriere die Vorgabe und mache dein eigenes Ding
3) Mach' den Scheiß mit

1 ist natürlich am Besten aber in der Regel unrealistisch. 2 ist verlockend aber 3 ist in der Regel der bessere Ansatz. Da muss man einfach das Ego runter schlucken.

Zu Design Patterns, die Patterns zu lernen schult Dein Denken. Darüber hinaus sind die Dinger aber total gehypt. Versuche zu durchblicken was Agenten in mobilen Systemen machen. Dann wirst du a) erkennen dass viele mobile Lösungen unnötig aufwendig und grottig sind und b), dass der Agent als Metapher für so ziemlich jedes Pattern, dass dir jemals irgendwo begegnen wird funktioniert. Inklusive Proxy-Pattern.
 
@ Kamikaze: danke fuer die (interessanten) Hinweise und Denkanstoesze.
@ Vincent Vega: Deine Anregung den mnemonisch dargestellten Maschinencode zu analysieren hat mir ermoeglicht ein Tor zur Erkenntnis zu oeffnen (und zu neuen Stufen). Danke! Ich habe mich weiterntwickelt. Manchmal sieht man den Wald vor lauter Baeumen nicht. :)

Nun denn, ich habe mir die Muehe gamacht, mir (in den vergangenen drei Tagen) einen oberflaechlichen Crashkurs in Assembler zu verpassen und den aus meinem ersten Posting enthaltenen Quellcode in Assembler zu uebersetzen. Nach (halbwegs) selbstkritischer Betrachtung (aufbauend auf Kamikazes Anregungen) kristallisierte sich folgendes Resultat:
Code:
struct protoent {                 
	uint16_t type;
#define DOWNSTREAM  0x0000;
#define ORPHAN  0xffff
	hook_p 	hook;		
};

struct ng_etype_split_softc {									 	
	struct protoent	sc_arp;
	struct protoent	sc_ip;
	struct protoent	sc_pppoe;
	struct protoent	sc_pppoedisc;
	struct protoent	sc_rarp;
	
	struct protoent sc_orphan;
	struct protoent	sc_downstream;
	
	/* ... */ 
	 		
	node_p 				node;		 	
};
typedef struct ng_etype_split_softc *sc_p;

/* ... Ausgeblendete Netgraph Methoden. */

/*
 * Frame reception.
 */
static int
ng_etype_split_rcvdata(hook_p hook, item_p item)
{ 
	uint16_t type;
	int err = 0;
	int len;
	const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
	const struct protoent *src = NG_HOOK_PRIVATE(hook);
	struct mbuf *m = NULL;
	struct ether_header *eh;
	struct protoent *dst = NULL;
	
	if (!src) {
		err = ENETDOWN;
		goto bad;
	} 
 			
	NGI_GET_M(item, m);
	if (m->m_pkthdr.len < ETHER_HDR_LEN) { 
		err = EINVAL;
		goto bad;
	}
	
	/* If contained PDU by message primitive is contiguous. */
	if (m->m_len < ETHER_HDR_LEN) {
		m = m_pullup(m, ETHER_HDR_LEN);
		if (!m) {
			err = ENOBUFS;
			goto bad;
		} 
	}
	
	len = m->m_pkthdr.len;
	eh = mtod(m, struct ether_header *);
	type = ntohs(eh->ether_type);
	
	switch (src->type) {
	case ETHERTYPE_ARP:		
	case ETHERTYPE_IP:
	case ETHERTYPE_PPPOE:	
	case ETHERTYPE_PPPOEDISC:		
	case ETHERTYPE_REVARP:
		if (src->type == type) 
			dst = &sc->sc_downstream;	
		else 
			err = EINVAL;	
		
		break;
	case ORPHAN:
		dst = &sc->sc_downstream;
		
		break;	
	default: /* If frame comes from downstream... */
		if (type == ETHERTYPE_ARP)		
			dst = &sc->sc_arp;
		else if (type == ETHERTYPE_IP)
			dst = &sc->sc_ip;
		else if (type == ETHERTYPE_PPPOE)	
			dst = &sc->sc_pppoe;
		else if (type == ETHERTYPE_PPPOEDISC)		
			dst = &sc->sc_pppoedisc;	
		else if (type == ETHERTYPE_REVARP)
			dst = &sc->sc_rarp;
		else
			dst = &sc->sc_orphan;
	/*
	 * ... every mbuf chain may be transformed to single message
	 * primitives for further processing. By the way, the PDU is 
	 * stored in an external buffer (cluster). 
	 *
	 * But, received single message primitives may not be touched.
	 */
		if (m->m_next) { 	
			char buf[len]; /* XXX: ??? */
			struct mbuf *n;	
				
			MGET(n, M_NOWAIT, MT_DATA);
			if (!n) 
				err = ENOBUFS;	
			else {
				MCLGET(n, M_NOWAIT);
				if (!(n->m_flags & M_EXT)) {
					m_freem(n);
					err = ENOBUFS; 
				} else {
					m_move_pkthdr(n, m);
					m_copydata(m, 0, len, buf);
					m_freem(m);	
					
					if (!m_append(n, len, buf)) {
						m_freem(n);
						err = ENOBUFS; 	
					} else 
						m = n;	
				}
			}
		} 
	}
		
	if (!dst->hook)
		err = EINVAL;	
bad:
	if (err) {	
		NG_FREE_M(m);
		if (item)
			NG_FREE_ITEM(item);
	} else 	
		NG_FWD_NEW_DATA(err, item, dst->hook, m);
		
	return(err);
}
bzw. das Resultat in Assembler (wobei ich der Uebersicht halt halber die Inlinefunktionen entfernt habe)
Code:
	.file	"ng_etype_split_rcvdata_version_neu.c"
#APP
	.globl __start_set_pcpu
	.globl __stop_set_pcpu
#NO_APP
	.text
	.p2align 4,,15
	.type	ng_etype_split_rcvdata, @function
ng_etype_split_rcvdata:
	pushl	%ebp				;			(prolog)
	movl	%esp, %ebp			;			(prolog: SFP)
	subl	$80, %esp			;				(alloc: 80x4 byte)
	movl	8(%ebp), %eax			; %eax = hook 		(fn_arg: hook_p hook)				
	movl	%eax, -48(%ebp)			; -48(%ebp) = hook	
	movl	12(%ebp), %eax			; %eax = item		(fn_arg: item_p item)
	movl	%eax, -52(%ebp)			; -52(%ebp) = item
	movl	__stack_chk_guard, %eax	
	movl	%eax, -4(%ebp)			; -4(%ebp) = __stack_chk_guard
	xorl	%eax, %eax			; %eax = 0
	movl	$0, -12(%ebp)			; int err = 0
	movl	-48(%ebp), %eax			; %eax = hook
	movl	48(%eax), %eax			; %eax = NG_HOOK_NODE(%eax)
	movl	44(%eax), %eax			; %eax = NG_NODE_PRIVATE(%eax)
	movl	%eax, -20(%ebp)			; const sc_p sc = %eax
	movl	-48(%ebp), %eax			; %eax = hook
	movl	32(%eax), %eax			; %eax = NG_HOOK_PRIVATE(%eax)
	movl	%eax, -24(%ebp)			; const struct protoent *src = %eax
	movl	$0, -28(%ebp)			; struct mbuf *m = NULL
    movl	$0, -36(%ebp)			; struct protoent *dst = NULL	
	cmpl	$0, -24(%ebp)			; if (!src)
	jne	.L2				;	... false => .L2 
	movl	$50, -12(%ebp)			; err = ENETDOWN
	jmp	.L4				;	=> .L4 (goto bad)
.L2:
	movl	-52(%ebp), %eax			; %eax = item [NGI_GET_M: Start]
	movl	16(%eax), %eax			; %eax = item->body.da_m
	movl	%eax, -28(%ebp)			; m = %eax
	movl	-52(%ebp), %eax			; %eax = item
	movl	$0, 16(%eax)			; item->body.da_m = NULL [Stop]
	movl	-28(%ebp), %eax			; %eax = m
	movl	32(%eax), %eax			; %eax = m->m_pkthdr.len
	cmpl	$13, %eax			; if (%eax < ETHER_HDR_LEN)
	jg	.L5				;	... false => .L5 
	movl	$22, -12(%ebp)			; err = EINVAL
	jmp	.L4				;	=> .L4 (goto bad)
.L5:
	movl	-28(%ebp), %eax			; %eax = m
	movl	12(%eax), %eax			; %eax = m->m_len
	cmpl	$13, %eax				; if (%eax < ETHER_HDR_LEN)
	jg	.L7							;	... false => .L7 
	movl	$14, 4(%esp)			; 4(%esp) = ETHER_HDR_LEN (fn_arg: int)
	movl	-28(%ebp), %eax			; %eax = m
	movl	%eax, (%esp)			; (%esp) = m (fn_arg: struct mbuf *)
	call	m_pullup				; %eax = m_pullup((%esp), 4(%esp))
	movl	%eax, -28(%ebp)			; m = %eax
	cmpl	$0, -28(%ebp)			; if (!m)
	jne	.L7				;	... false => .L7 	
	movl	$55, -12(%ebp)			; err = ENOBUFS
	jmp	.L4				;	=> .L4 (goto bad)
.L7:
	movl	-28(%ebp), %eax			; %eax = m
	movl	32(%eax), %eax			; %eax = m->m_pkthdr.len
	movl	%eax, -16(%ebp)			; len = %eax
	movl	-28(%ebp), %eax			; %eax = m
	movl	8(%eax), %eax			; %eax = mtod(%eax, struct ether_header *)
	movl	%eax, -32(%ebp)			; eh = %eax
	movl	-32(%ebp), %eax			; %eax = eh
	movzwl	12(%eax), %eax			; %ax = eh->ether_type
	movzwl	%ax, %eax			; %eax = %ax
	movl	%eax, (%esp)			; (%esp) = eh->ether_type (fn_arg: uint16_t)
	call	__bswap16_var			; %ax = ntohs((%esp))
	movw	%ax, -6(%ebp)			; type = %ax
	movl	-24(%ebp), %eax			; %eax = src
	movzwl	(%eax), %eax			; %ax = src->type
	movzwl	%ax, %eax			; %eax = %ax
	movl	%eax, -64(%ebp)			; -64(%ebp) = %ax (temporary: src->type) => switch(-68(%ebp))	
	cmpl	$32821, -64(%ebp)		;	
	je	.L11				;	||
	cmpl	$32821, -64(%ebp)		;	||
	jg	.L13				;	\/
	cmpl	$2048, -64(%ebp)		;  
	je	.L11				; fallthrough => .L11 | .L13 (proto_hook_rcvdata)
	cmpl	$2054, -64(%ebp)		;
	je	.L11				;____________________________
	jmp	.L10				; default: => .L10 (downstream_hook_rcvdata)
.L13:								
	cmpl	$34915, -64(%ebp)		;	||
	jl	.L10				;	||
	cmpl	$34916, -64(%ebp)		;	\/
	jle	.L11				; fallthrough => .L11 (proto_hook_rcvdata)
	cmpl	$65535, -64(%ebp)		;	
	je	.L12				; case ORPHAN: => .L12 (orphan_hook_rcvdata)
	jmp	.L10				; default: => .L10
.L11:								
	movl	-24(%ebp), %eax			; %eax = src
	movzwl	(%eax), %eax			; %ax = src->type
	cmpw	-6(%ebp), %ax			; if (%ax == type)
	jne	.L14				;	... false => .L14 (wrong_frm_type)
	movl	-20(%ebp), %eax			; %eax = sc
	addl	$48, %eax			; %eax = sc->sc_downstream
	movl	%eax, -36(%ebp)			; dst = %eax
	jmp	.L17				;	=> .L17 (break)
.L14:
	movl	$22, -12(%ebp)			; err = EINVAL
	jmp	.L17				;	=> .L17 (break)
.L12:
	movl	-20(%ebp), %eax			; %eax = sc
	addl	$48, %eax			; %eax = sc->sc_downstream
	movl	%eax, -36(%ebp)			; dst = %eax
	jmp	.L17				;	=> .L17 (break)
.L10:							
	cmpw	$2054, -6(%ebp)			; if (type == ETHERTYPE_ARP)
	jne	.L18				;	... false => .L18 (else if)
	movl	-20(%ebp), %eax			; %eax = sc->sc_arp
	movl	%eax, -36(%ebp)			; dst =	%eax; 
	jmp	.L20				;	=> .L20 
.L18:
	cmpw	$2048, -6(%ebp)			; else if (type == ETHERTYPE_IP)
	jne	.L21				;	... false => .L12 (else if)
	movl	-20(%ebp), %eax			; %eax = sc
	addl	$8, %eax			; %eax = sc->sc_arp
	movl	%eax, -36(%ebp)			; dst = %eax 
	jmp	.L20				;	=> .L20
.L21:		
	cmpw	$-30620, -6(%ebp)		; else if (type == ETHERTYPE_PPPOE)
	jne	.L23				;	... false => .L23 (else if)
	movl	-20(%ebp), %eax			; %eax = sc
	addl	$16, %eax			; %eac = sc->sc_pppoe
	movl	%eax, -36(%ebp)			; dst = %eax
	jmp	.L20				;	=> .L20
.L23:
	cmpw	$-30621, -6(%ebp)		; else if (type == ETHERTYPE_PPPOEDISC
	jne	.L25				;	... false => .L25 (else if)
	movl	-20(%ebp), %eax			; %eax = sc
	addl	$24, %eax			; %eax = sc->sc_pppoedisc
	movl	%eax, -36(%ebp)			; dst = %eax
	jmp	.L20				;	=> .L20
.L25:
	cmpw	$-32715, -6(%ebp)		; else if (type == ETHERTYPE_REVARP)
	jne	.L27				;	... false => .L27 (else)
	movl	-20(%ebp), %eax			; %eax = sc
	addl	$32, %eax			; %eax = sc->sc_revarp
	movl	%eax, -36(%ebp)			; dst = %eax
	jmp	.L20				; 	=> .L20
.L27:	
	movl	-20(%ebp), %eax			; else => %eax = sc
	addl	$40, %eax			; 	%eax = sc->sc_orphan
	movl	%eax, -36(%ebp)			;	dst = %eax
.L20:
	movl	-28(%ebp), %eax			; %eax = m
	movl	(%eax), %eax			; %eax = m->m_next 
	testl	%eax, %eax			; if (%eax)
	je	.L17				;	... false => .L17 
	movl	%esp, %eax			; %eax = ESP ??? 
	movl	%eax, -56(%ebp)			; ??? 
	movl	-16(%ebp), %eax			; %eax = len
	addl	$15, %eax			; ???
	addl	$3, %eax			; ?? 
	shrl	$2, %eax			; ??
	sall	$2, %eax			; ?? Da existieren noch einige offene Fragen
	subl	%eax, %esp			; ?? bzgl. automat Alloaktion 
	leal	16(%esp), %edx			; ??   
	movl	%edx, -60(%ebp)			; ?? 	von char buf[len]
	movl	-60(%ebp), %eax			; ?? 
	addl	$15, %eax			; ?? nach 3 Tagen autodidaktischen	  
	shrl	$4, %eax			; ?? Assemblercrashkurs, ist es mir unmoeglich			
	sall	$4, %eax			; ?? (bzgl. Analyse von Maschinencode) perfekt  		
	movl	%eax, -60(%ebp)			; ?? zu sein, aber ich lerne schnell!
	movl	-60(%ebp), %eax			; ??
	movl	%eax, -44(%ebp)			; buf = %eax (???)
	movl	$1, 4(%esp)			; 4(%esp) = MT_DATA	(fn_arg; int)
	movl	$1, (%esp)			; (%esp) = M_NOWAIT	(fn_arg; int)
	call	m_get				; %eax = m_get((%esp), 4(%esp))
	movl	%eax, -40(%ebp)			; struct mbuf *n = %eax
	cmpl	$0, -40(%ebp)			; if (!n)
	jne	.L30				;	... false => .L30 (MCLGET(...))
	movl	$55, -12(%ebp)			; err = ENOBUFS
	jmp	.L32				;	=> .L32 
.L30:
	movl	$1, 4(%esp)			; 4(%esp) = M_NOWAIT (fn_arg: int) 
	movl	-40(%ebp), %eax			; %eax = n
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_clget				; %eax = m_clget((%esp), 4(%esp))
	movl	-40(%ebp), %eax			; n = %eax
	movl	16(%eax), %eax			; %eax = n->m_flags
	andl	$1, %eax			; %eax = %eax & 1
	testl	%eax, %eax			; if (!%eax)
	jne	.L33				;	... false => .L33 (message primitive transformation)
	movl	-40(%ebp), %eax			; %eax = n
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_freem				; m_freem((%esp))
	movl	$55, -12(%ebp)			; err = ENOBUFS
	jmp	.L32				;	=> .L32
.L33:
	movl	-28(%ebp), %eax			; %eax = m
	movl	%eax, 4(%esp)			; 4(%esp) = %eax (fn_arg: struct mbuf *)
	movl	-40(%ebp), %eax			; %eax = n
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_move_pkthdr			; m_move_pkthdr((%esp), 4(%esp))
	movl	-44(%ebp), %eax			; %eax = buf 	
	movl	%eax, 12(%esp)			; 12(%esp) (fn_arg: caddr_t)
	movl	-16(%ebp), %eax			; %eax = len
	movl	%eax, 8(%esp)			; 8(%esp) = %eax (fn_arg: int)
	movl	$0, 4(%esp)			; 4(%esp) = 0 (fn_arg: int) ... offset
	movl	-28(%ebp), %eax			; %eax = m
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_copydata			; m_copydata((%esp), 4(%esp), 8(%esp), 12(%esp))
	movl	-28(%ebp), %eax			; %eax = m
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_freem				; m_freem((%esp))
	movl	-44(%ebp), %eax			; %eax = buf
	movl	%eax, 8(%esp)			; 8(%esp) = %eax (fn_arg: caddr_t)
	movl	-16(%ebp), %eax			; %eax = len
	movl	%eax, 4(%esp)			; 4(%esp) = %eax (fn_arg: int)
	movl	-40(%ebp), %eax			; %eax = n
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_append			; %eax = m_append((%esp), 4(%esp), 8(%esp))
	testl	%eax, %eax			; if (%eax == 0)
	jne	.L35				;	... false => .L35
	movl	-40(%ebp), %eax			; %eax = n
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *m)
	call	m_freem				; m_freem((%esp))
	movl	$55, -12(%ebp)			; err = ENOBUFS
	jmp	.L32				;	=> .L32
.L35:		
	movl	-40(%ebp), %eax			; %eax = n
	movl	%eax, -28(%ebp)			; m = %eax
.L32:
	movl	-56(%ebp), %esp			; ???
.L17:
	movl	-36(%ebp), %eax			; %eax = dst
	movl	4(%eax), %eax			; %eax = dst->hook
	testl	%eax, %eax			; if (!dst->hook) ... means %eax & %eax
	jne	.L4				;	... false => .L4 
	movl	$22, -12(%ebp)			; err = EINVAL
.L4:					
	cmpl	$0, -12(%ebp)			; if (err)
	je	.L38				;	... false => .L38 (NG_FWD_NEW_DATA(...))
	cmpl	$0, -28(%ebp)			; if (!m)
	je	.L40				;	... false => .L40 (NG_FREE_ITEM(...))
	movl	-28(%ebp), %eax			; %eax = m
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: struct mbuf *)
	call	m_freem				; m_freem((%eax)
	movl	$0, -28(%ebp)			; m = NULL
.L40:
	cmpl	$0, -52(%ebp)			; if (item)
	je	.L44				;	... false => .L44 (epilog)
	movl	-52(%ebp), %eax			; %eax = item
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: item_p)
	call	ng_free_item			; ng_free_item((%esp))
	jmp	.L44				;	=> .L44 (epilog)
.L38:
	movl	-52(%ebp), %edx			; %edx = item
	movl	-28(%ebp), %eax			; %eax = m
	movl	%eax, 16(%edx)			; item->body.da_m = %eax
	movl	$0, -28(%ebp)			; m = NULL
	movl	-36(%ebp), %eax			; %eax = dst
	movl	4(%eax), %eax			; %eax = dst->hook
	movl	$0, 12(%esp)			; 12(%esp) = NG_NOFLAGS (fn_arg: int)
	movl	%eax, 8(%esp)			; 8(%esp) = %eax (fn_arg: hook_p)
	movl	-52(%ebp), %eax			; %eax = item
	movl	%eax, 4(%esp)			; 4(%esp) = %eax (fn_arg: item_p)
	movl	$0, (%esp)			; (%esp) = err (fn_arg: int)
	call	ng_address_hook			; ng_address_hook((%esp), 4(%esp), 8(%esp), 12(%esp))
	movl	%eax, -12(%ebp)			; %eax = err;
	cmpl	$0, -12(%ebp)			; if (err)
	jne	.L45				;	... false => .L52 (cleanup)
	movl	$0, 4(%esp)			; 4(%esp) = NG_NOFLAGS (fn_arg: int)
	movl	-52(%ebp), %eax			; %eax = item
	movl	%eax, (%esp)			; (%esp) = %eax (fn_arg: item_p)
	call	ng_snd_item			; %eax = ng_snd_item((%esp), 4(%esp)
	movl	%eax, -12(%ebp)			; err = %eax
.L45:
	movl	$0, -52(%ebp)			; item = NULL
.L44:
	movl	-12(%ebp), %eax			; %eax = err (Rueckgabewert von return(...)
	movl	-4(%ebp), %edx			; %edx = __stack_chk_guard
	xorl	__stack_chk_guard, %edx		; __stack_chk_guard XOR %edx
	je	.L48				;	... false => .L48 (epilog)
	call	__stack_chk_fail		; => Ueberlauf!!! ==> bspw. panic() 
.L48:						; (epilog)		
	leave					; (epilog)
	ret
Fazit: Lego! :D

Irgendwie kommt es mir so vor, als ob sich mein raeumliches Vorstellungsvermoegen gesteigert wurde.
 
Zuletzt bearbeitet von einem Moderator:
Erst mal eine Frage, was macht ein static bei einer Funktion in C?

Dann, so weit ich das überblicke wird nur lesend auf hook und item zugegriffen. Ich bin mir wegen der Macros nicht sicher. Falls dem so ist, sollten sie aber beide const sein.

Dann ist da noch die Frage ob hook und item structs oder primitive typen sind. Ggf. wären Pointer hier deutlich schneller.
 
Der Code ist (bei weiten) nicht perfekt und dient dem Zweck der Selbsterkenntniss, aber wenn er schon mal geschrieben ist, dann kann ich diesen auch mit der Welt teilen, weil ich (aus Ueberzeugung) ein Freund von freier Bildung, Informationen und Wissen bin (insbesonders dann, wenn dieses Mitmenschen Hilfe zur Selbsthilfe bieten kann und dem gesellschaftlichen und technischen Fortschritt dient).
Code:
/*-
 *
 * Copyright (c) 2012, das.chaos 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * ng_etype_split.c
 *
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/ctype.h>
#include <sys/errno.h>
#include <sys/syslog.h>

#include <netgraph/ng_message.h>
#include <netgraph/ng_parse.h>
#include "ng_etype_split.h"
#include <netgraph/netgraph.h>

#include <net/ethernet.h>

/* See malloc(9) for further details. */
MALLOC_DEFINE(M_NETGRAPH_ETYPE_SPLIT, "netgraph etype split", "netgraph etype split node");

/* 
 * Describes a hook, but feel free how to implement this structure,  
 * i. e. it is possible to implement recursive data structures by using 
 * macros discribed in queue(3) or tree(3). 
 */ 
struct protoent {                 
	uint16_t 	type;	/* Hook type determined here by Frame type, i. e. IEEE802.3XXX stuff */
#define DOWNSTREAM 0x0000;
#define ORPHAN 0xffff
	hook_p 	hook;		/* Represents a transition to nodes peer entity */
};

/* 
 * Nodes private data example, all containing items shows 
 * a possible configuration. 
 */
struct ng_etype_split_softc {									 	
	struct protoent	sc_arp;
	struct protoent	sc_ip;
	struct protoent	sc_pppoe;
	struct protoent	sc_pppoedisc;
	struct protoent	sc_rarp;

	struct protoent 	sc_orphan;
	struct protoent	sc_downstream;
	
	node_p node;	/* Backpointer to active instance */ 

	/* 
	 * XXX: here is enough space for statistical data,
	 * 	queues, hashtables and other homebrew stuff. 
	 */		 	
};
typedef struct ng_etype_split_softc *sc_p;

/* Public Netgraph methods */
static ng_constructor_t		ng_etype_split_constructor;
static ng_shutdown_t		ng_etype_split_shutdown;
static ng_newhook_t		ng_etype_split_newhook;
static ng_rcvdata_t		ng_etype_split_rcvdata;
static ng_disconnect_t		ng_etype_split_disconnect;

/* Node type, service access point. */
static struct ng_type typestruct = {
	.version 	= NG_ABI_VERSION,
	.name 		= NG_ETYPE_SPLIT_NODE_TYPE,
	.constructor 	= ng_etype_split_constructor,
	.shutdown 	= ng_etype_split_shutdown,
	.newhook 	= ng_etype_split_newhook,
	.rcvdata 	= ng_etype_split_rcvdata,
	.disconnect 	= ng_etype_split_disconnect,
};
NETGRAPH_INIT(ng_etype_split, &typestruct);

/*
 * Constructs a node.
 */ 
static int
ng_etype_split_constructor(node_p node)
{ 
	sc_p sc;

        sc = malloc(sizeof(*sc), M_NETGRAPH_ETYPE_SPLIT, M_NOWAIT | M_ZERO);
	if (!sc) 
		return(ENOMEM);
   
	NG_NODE_SET_PRIVATE(node, sc);
	sc->node = node;  	 
   
	return(0);
}

/* 
 * Node destructor.
 */
static int
ng_etype_split_shutdown(node_p node)
{
	const sc_p sc = NG_NODE_PRIVATE(node);
				
	NG_NODE_SET_PRIVATE(node, NULL);
	NG_NODE_UNREF(node);
	free(sc, M_NETGRAPH_ETYPE_SPLIT)

	return(0);
}

/*
 * Initializes a transition to the hook of its peer instance. 
 */
static int
ng_etype_split_newhook(node_p node, hook_p hook, const char *name)
{ 
	int err = 0;
	const sc_p sc = NG_NODE_PRIVATE(node);
	struct protoent *pep = NULL;
	
	if (strcmp(name, NG_ETYPE_SPLIT_HOOK_DOWNSTREAM) == 0) {    
	   	sc->sc_downstream.type = DOWNSTREAM;
	   	sc->sc_downstream.hook = hook;  
	   	pep = &sc->sc_downstream; 
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_ORPHAN) == 0) {    
	   	sc->sc_orphan.type = ORPHAN;
	   	sc->sc_orphan.hook = hook;  
	   	pep = &sc->sc_orphan;   
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_ARP) == 0) {    
	   	sc->sc_arp.type = ETHERTYPE_ARP;
	   	sc->sc_arp.hook = hook;  
		pep = &sc->sc_arp;
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_IP) == 0) {    
	   	sc->sc_ip.type = ETHERTYPE_ARP;
	   	sc->sc_ip.hook = hook;  
		pep = &sc->sc_ip;   
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_PPPOE) == 0) {    
	   	sc->sc_pppoe.type = ETHERTYPE_PPPOE;
	   	sc->sc_pppoe.hook = hook;  
		pep = &sc->sc_pppoe;   
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_PPPOEDISC) == 0) {    
	   	sc->sc_pppoedisc.type = ETHERTYPE_PPPOEDISC;
	   	sc->sc_pppoedisc.hook = hook;  
		pep = &sc->sc_pppoedisc;   
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_REVARP) == 0) {    
	 	sc->sc_rarp.type = ETHERTYPE_REVARP;
		sc->sc_rarp.hook = hook;  
		pep = &sc->sc_orphan;      
	} 		
	
	if (pep)
		NG_HOOK_SET_PRIVATE(hook, pep);
	else
		err = EINVAL;	
		
	return(err);
}

/*
 * Data reception. 
 */
static int
ng_etype_split_rcvdata(hook_p hook, item_p item)
{ 
	uint16_t type;
	int err = 0;
	int len;
        const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
	const struct protoent *src = NG_HOOK_PRIVATE(hook);
	struct mbuf *m = NULL;
	struct enet_hdr *eh;
	struct protoent *dst = NULL;

	if (!src) {
		err = ENETDOWN;
		goto bad;
	} 
 			
	NGI_GET_M(item, m);
	if (m->m_pkthdr.len < ETHER_HDR_LEN) { 
		err = EINVAL;
		goto bad;
	}
	
	/* If contained frame (PDU) of message primitive is contiguous? */
	if (m->m_len < ETHER_HDR_LEN) {
		m = m_pullup(m, ETHER_HDR_LEN);
		if (!m) {
			err = ENOBUFS;
			goto bad;
		} 
	}
	
	len = m->m_pkthdr.len;
	eh = mtod(m, struct enet_hdr *);
	type = ntohs(eh->eh_type);
	
	switch (src->type) {
	case ETHERTYPE_ARP:		
	case ETHERTYPE_IP:
	case ETHERTYPE_PPPOE:	
	case ETHERTYPE_PPPOEDISC:		
	case ETHERTYPE_REVARP:
		if (src->type == type) 
			dst = &sc->sc_downstream;	
		else 
			err = EINVAL;	
		
		break;
	case ORPHAN:
		dst = &sc->sc_downstream;
		
		break;	
	default: /* If frame comes from downstream... */
		if (type == ETHERTYPE_ARP) 	
			dst = &sc->sc_arp;
		else if (type == ETHERTYPE_IP) 
			dst = &sc->sc_ip;
		else if (type == ETHERTYPE_PPPOE) 
			dst = &sc->sc_pppoe;
		else if (type == ETHERTYPE_PPPOEDISC)	
			dst = &sc->sc_pppoedisc;	
		else if (type == ETHERTYPE_REVARP)
			dst = &sc->sc_rarp;
		 else 
			dst = &sc->sc_orphan;
	/*
	 * ... every mbuf chain may be transformed to single message
	 * primitives for further processing. By the way, the PDU is 
	 * stored in an external buffer (cluster). 
	 *
	 * But, received single message primitives may not be touched.
	 */
		if (m->m_next) { 
			char buf[len];
			struct mbuf *n;	
		 	
		 	MGET(n, M_NOWAIT, MT_DATA);
			if (!n) 
				err = ENOBUFS;	
			else {
				MCLGET(n, M_NOWAIT);
				if (!(n->m_flags & M_EXT)) {
					m_freem(n);
					err = ENOBUFS; 
				} else {
					m_move_pkthdr(n, m);
					m_copydata(m, 0, len, buf);
					m_freem(m);	
					
					if (!m_append(n, len, buf)) {
						m_freem(n);
						err = ENOBUFS; 	
					} else 
						m = n;
				}
			}							
		}	
	}
	
	if (!dst->hook)
		err = EINVAL;
bad:
	if (err) {
		NG_FREE_M(m);
		if (item)
			NG_FREE_ITEM(item);
	} else
		NG_FWD_NEW_DATA(err, item, dst->hook, m);	
		
	return(err);
}

/*
 * Hook disconnection. If there exists no hook, 
 * this node becomes destroyed.
 */
static int
ng_etype_split_disconnect(hook_p hook)
{ 
	struct protoent *pep = NG_HOOK_PRIVATE(hook);
	
	if (pep) 
		pep->hook = NULL;		
		
	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 
	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) 
		ng_rmnode_self(NG_HOOK_NODE(hook));
	
	return(0);
}
Die zugehoerige headerdatei:
Code:
#define _NETGRAPH_NG_ETYPE_SPLIT_H_

/* Node type name. */
#define NG_ETYPE_SPLIT_NODE_TYPE	"etype_split"

/* Node type cookie. */
#define NGM_ETYPE_SPLIT_COOKIE		1327306053

/* Example hook names */
#define NG_ETYPE_SPLIT_HOOK_DOWNSTREAM	"downstream"
#define NG_ETYPE_SPLIT_HOOK_ORPHAN	"orphan"
#define NG_ETYPE_SPLIT_HOOK_ARP		"arp"
#define NG_ETYPE_SPLIT_HOOK_IP		"ip"
#define NG_ETYPE_SPLIT_HOOK_PPPOE	"pppoe"
#define NG_ETYPE_SPLIT_HOOK_PPPOEDISC	"pppoedisc"
#define NG_ETYPE_SPLIT_HOOK_REVARP	"revarp"
bzw. die zugehoerige Makefile
Code:
KMOD= 	ng_etype_split
SRCS=	ng_etype_split.c 
		
.include  <bsd.kmod.mk>
Ferner,
Code:
	char buf[len];
kann ersertezt werden durch
Code:
if (m->m_next) { 
	char *buf;
	struct mbuf *n;	
		 	
	MGET(n, M_NOWAIT, MT_DATA);
	if (!n) 
		err = ENOBUFS;	
	else {
		MCLGET(n, M_NOWAIT);
		if (!(n->m_flags & M_EXT)) {
			m_freem(n);
			err = ENOBUFS; 
		} else {
			buf = malloc(len, M_NETGRAPH_ETYPE_SPLIT, M_NOWAIT | M_ZERO);
			if (!buf)
				err = ENOMEM;
			else {
				m_move_pkthdr(n, m);
				m_copydata(m, 0, len, buf);
				m_freem(m);	
				
				if (!m_append(n, len, buf)) {
					m_freem(n);
					err = ENOBUFS; 	
				} else 
					m = n;
				
				free(buf, M_NETGRAPH_ETYPE_SPLIT);
			}
		}
	}							
}
oder alternativ o. g. Codeschnipsel kann komplett ersetzt werden mittels das Verwenden von m_unshare(9), denn o. g. Code tranformiert Mbuf chains nur in Einzelne Cluster der Groesze von 2048 Byte (minimale Clustergroesze), wenn PDUs mit einer Laenge kleiner gleich max. genannter Clustergroesze in Erscheinung treten (sonst werden wieder Verkettete Listen erzeugt).
Code:
struct mbuf *n;

/* ... */

n = m_unshare(m, M_NOWAIT)
if (n)
	m = n;
Wenn bspw. sog. Babyjumbo- / Jumboframes (bspw. MPLS oder FCoE) und oder andere Widrigkeiten in Erscheinung treten koennten, erweist sich das Verarbeiten von Message Primitives mit m_unshare() vorteilhaft. Das Datenfeld ist dann beschreibbar und es ist garantiert, dasz verkettete Listen von maximal zwei Elementen erzeugt werden.

Entweder wird ein Mbuf mit M_EXT Flag und zugehoerigen Cluster (Groesze von 2048 ... 16184 Byte) erzeugt (hier ist dann die komplette PDU im Datenfeld des Clusters enthalten) oder eine verkettete Liste mit einem Mbuf bereits erwaehnten Cluster und einem Mbuf mit M_PKTHDR Flag als Listenkopf, welches die PCI (Frameheader) einer PDU im (internen) Datenfeld enthaelt. Die SDU ist dann im Datenfeld des Clusters (des zweiten Mbufs) enthalten.

Wie kann man denn nun mit diesem Netgraphknoten (quick and dirty!!!) umspringen? Ein der Shellsyntax entlehntes Beispiel sollte fuer etwas Erhellung sorgen: Sei ein Switch mit bspw. 7 NICs von denen 3 als sogennante freie NICs operieren:
Code:
kldload ng_socket.ko
kldload ng_ether.ko
kldload ng_bridge.ko 
kldload ng_etype_split.ko
Es existiert intern eine verbrueckende Entitaetet (relay0)
Code:
if_table_relay0='fxp1 fxp2 fxp3'
if_table_first='fxp0'
if_table_free='fxp4 fxp5 fxp6'
if_table_all="${if_table_relay_0} ${if_table_first} ${if_table_free}"
erzeugen eines Relays
Code:
relay_create() {
 	local if_first=${if_first_tmp}	 
	local if_table=${if_table_tmp}
	local relay_name=${relay_name_tmp}
	local link_num=1

 	ngctl ${if_first}: bridge lower link0
 	ngctl name ${if_first}:lower ${relay_name} 

 	for if_ent in ${if_table}; do
		ngctl connect ${if_ent}: ${relay_name} lower link${link_num}    		
		link_num=$(expr ${link_num} + 1)
	done
}
implizit das Relay erzeugen
Code:
if_first_tmp=fxp0
if_table_tmp=${if_table_relay0}
relay_name_tmp=relay0
relay_create
	.
	.
	.
usw.usf. ...
	.
bspw. das Relay mit ng_etype_split in Beziehung zu den freien NICs stellen
Code:
ngctl relay0: ng_etype_split link4 downstream
ngctl name relay0:link4 split0

ngctl connect split0: fxp4 orphan downstream
ngctl connect split0: fxp5 pppoe downstream
ngctl connect split0: fxp6 pppoedisc downstream

for if_ent in ${if_table_all}; do
	ngctl msg ${if_ent}: setpromisc 1
	ngctl msg ${if_ent}: setautosrc 0
done
Was alles moeglich erscheinen koennte (bspw. Baeume bzw. Waelder von Bruecken etc.pp.) sei der Phantasie des geneigten Lesers (oder auch Leserin :)) ueberlassen. Ich uebernehme keine Haftung fuer entstehende Stoerungen der oeffentlichen Ordnung und resultierenden Schaden sowie moegliche Folgeschaeden bei allgemeinen Gebrauch (siehe Haftungsausschluss im o. a. Lizenzvermerk).
 
Zuletzt bearbeitet von einem Moderator:
Habe etwas Refactoring betrieben.
Code:
/*-
 *
 * Copyright (c) 2013, das.chaos 
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice unmodified, this list of conditions, and the following
 *    disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * ng_etype_split.c
 *
 */
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/ctype.h>
#include <sys/errno.h>
#include <sys/syslog.h>

#include <net/ethernet.h>

#include <netgraph/ng_message.h>
#include <netgraph/ng_parse.h>
#include "ng_etype_split.h"
#include <netgraph/netgraph.h>

#define MB_FRAGS_MAX 48

struct etype {
	uint16_t	type;
#define DOWNSTREAM 0x0000;
#define ORPHANS 0xffff
	hook_p	hook;		
};

struct ng_etype_split_softc {									 	
	struct etype	sc_arp;
	struct etype	sc_ip;
	struct etype	sc_pppoe;
	struct etype	sc_pppoedisc;
	struct etype	sc_rarp;
	
	struct etype	sc_orphans;
	struct etype	sc_downstream;
	
	/* 
	 * XXX: Other attributes could be placed here. Feel free and enjoy... 
	 */
		
	node_p	node;		 	
};
typedef struct ng_etype_split_softc *ets_softc_p;

MALLOC_DEFINE(M_NG_ETYPE_SPLIT, "NG_ETYPE_SPLIT", "ng_etype_split: softc");

static int	ng_etype_split_mod_event(module_t, int, void *); 
static ng_constructor_t	ng_etype_split_constructor;
static ng_shutdown_t	ng_etype_split_shutdown;
static ng_newhook_t	ng_etype_split_newhook;
static ng_rcvdata_t	ng_etype_split_rcvdata;
static ng_connect_t	ng_etype_split_connect;
static ng_disconnect_t	ng_etype_split_disconnect;

static struct ng_type typestruct = {
	.version 		=	NG_ABI_VERSION,
	.name 			=	NG_ETYPE_SPLIT_NODE_TYPE,
	.mod_event 		=	ng_etype_split_mod_event,
	.constructor 		=	ng_etype_split_constructor,
	.shutdown 		=	ng_etype_split_shutdown,
	.newhook 		=	ng_etype_split_newhook,
	.rcvdata 		=	ng_etype_split_rcvdata,
	.connect		=	ng_etype_split_connect,
	.disconnect 		=	ng_etype_split_disconnect,
};
NETGRAPH_INIT(ng_etype_split, &typestruct);

/*
 * Node constructor.
 */ 
static int
ng_etype_split_constructor(node_p node)
{ 
	ets_softc_p sc;

	sc = malloc(sizeof(*sc), M_NG_ETYPE_SPLIT, M_WAITOK | M_ZERO);
	
	sc->sc_downstream.type = DOWNSTREAM;
	sc->sc_orphans.type = ORPHANS;
	sc->sc_arp.type = ETHERTYPE_ARP;
	sc->sc_ip.type = ETHERTYPE_IP;
	sc->sc_pppoe.type = ETHERTYPE_PPPOE;
	sc->sc_pppoedisc.type = ETHERTYPE_PPPOEDISC;
	sc->sc_rarp.type = ETHERTYPE_REVARP;
	
	NG_NODE_SET_PRIVATE(node, sc);
	sc->node = node;  	 

	return(0);
}

/*
 * Hook initialization.
 */
static int
ng_etype_split_newhook(node_p node, hook_p hook, const char *name)
{ 	
	const ets_softc_p sc = NG_NODE_PRIVATE(node);
	struct etype *etp;

	if (strcmp(name, NG_ETYPE_SPLIT_HOOK_DOWNSTREAM) == 0) {    
	  	etp = &sc->sc_downstream;
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_ORPHANS) == 0) {    
		etp = &sc->sc_orphans;	   
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_ARP) == 0) {    
		etp = &sc->sc_arp;
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_IP) == 0) {     
	   	etp = &sc->sc_ip;	
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_PPPOE) == 0) {     
	  	etp = &sc->sc_pppoe;
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_PPPOEDISC) == 0) {      
	   	etp = &sc->sc_pppoedisc;
	} else if (strcmp(name, NG_ETYPE_SPLIT_HOOK_REVARP) == 0) {      
		etp = &sc->sc_rarp;
	} else {
		return(EINVAL);
	}
		
	etp->hook = hook;
	NG_HOOK_SET_PRIVATE(hook, etp);

	return(0);
}

static int
ng_etype_split_rcvdata(hook_p hook, item_p item)
{ 
	const ets_softc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
	const struct etype *src = NG_HOOK_PRIVATE(hook);
	
	int errno = EINVAL;
	
	struct mbuf *m = NULL;
	struct ether_header *eh;
	uint16_t type;
	hook_p dst;
	
	NGI_GET_M(item, m);
	if (m->m_pkthdr.len < ETHER_HDR_LEN) {
		goto bad;
	}
	
	if (m->m_len < ETHER_HDR_LEN) {
		m = m_pullup(m, ETHER_HDR_LEN);
		if (!m) {
			errno = ENOBUFS;
			goto bad;
		} 
	}
	
	eh = mtod(m, struct ether_header *);
	type = ntohs(eh->ether_type);

	switch (src->type) {
	case ETHERTYPE_ARP:		
	case ETHERTYPE_IP:
	case ETHERTYPE_PPPOE:	
	case ETHERTYPE_PPPOEDISC:
	case ETHERTYPE_REVARP:

		if (src->type != type) {
			goto bad;	
		} 
                            
	case ORPHANS:
		dst = sc->sc_downstream.hook;
		
		break;	
	default: /* If frame comes from downstream... */

		switch (type) {
		case ETHERTYPE_ARP:		
			dst = sc->sc_arp.hook;
			break;
		case ETHERTYPE_IP:
			dst = sc->sc_ip.hook;
			break;
		case ETHERTYPE_PPPOE:	
			dst = sc->sc_pppoe.hook;
			break;
		case ETHERTYPE_PPPOEDISC:		
			dst = sc->sc_pppoedisc.hook;	
			break;
		case ETHERTYPE_REVARP:
			dst = sc->sc_rarp.hook;
			break;
		default:
			dst = sc->sc_orphans.hook;
		}
		
#ifdef MBUF_KASPERLTHEATER		
		/*
		 * Ensures that existing mbuf chains might 
		 * become converted into clusters or to
		 * become the smallest chain as possible.
		 */
		if ((m->m_flags & M_EXT) == 0 
		&& m->m_pkthdr.len > MHLEN) { 
			n = m_collapse(m, M_DONTWAIT, MB_FRAGS_MAX);
			if (n == NULL) {
				m_freem(m);
				m = NULL;
				errno = ENOBUFS;				 		
				goto bad;
			} 
			m = n;
		}	
#endif
	}


	NG_FWD_NEW_DATA(errno, item, dst, m);	
	return(errno);

bad:
	NG_FREE_M(m);
	NG_FREE_ITEM(item);
	return(errno);
}
 
/* 
 * Destroys entire instance.
 */
static int
ng_etype_split_shutdown(node_p node)
{
	const ets_softc_p sc = NG_NODE_PRIVATE(node);
			
	NG_NODE_SET_PRIVATE(node, NULL);
	NG_NODE_UNREF(node);
	free(sc, M_NG_ETYPE_SPLIT);
	
	return(0);
}

/*
 * Hook connection.
 */
static int
ng_etype_split_connect(hook_p hook)
{
#ifdef MBUF_NOCH_MEHR_THEATER 
 /*
  * Enables per hook associated queueing of message primitives by setting 
  * denoted bit. Finally, processing of mp may becomes (more) synchronized 
  * with regular (concurrent) operating network stack.
  */
	NG_HOOK_FORCE_QUEUE(hook);
#endif
	return(0);
}

/*
 * Hook disconnection.
 *
 * For this type, removal of the last ng_etype_split_entry destroys the node.
 */
static int
ng_etype_split_disconnect(hook_p hook)
{ 
	struct etype *etp = NG_HOOK_PRIVATE(hook);
	
	etp->hook = NULL;		
	
	/* If there exists no hook, the node destructor takes place by callback. */	
	if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) 
	&& (NG_NODE_IS_VALID(NG_HOOK_NODE(hook)))) {
		ng_rmnode_self(NG_HOOK_NODE(hook));
	}
	return(0);
}

/*
 * Module event handler.
 */
static int
ng_etype_split_mod_event(module_t mod, int event, void *data)
{
	int errno = 0;

	switch (event) {
	case MOD_LOAD:
		break;
	case MOD_UNLOAD:			
		break;
	default:
		errno = EOPNOTSUPP;
	}
	return(errno);
}
Momentan experimentiere ich aus autodidaktischen Gruenden mit meinem Computer und da dachte ich mir die Aenderungen (getreu dem Motto: wird schon irgendwie fuer Irgendjemand praktisch sein - auch wenn dieses bisschen Code mehr oder weniger als kleine Spielerei zu betrachten ist) zu ergaenzen...
 
Zuletzt bearbeitet von einem Moderator:
Zurück
Oben