jQuery: Aufruf von Objekt-Methoden im Rahmen der Event-Behandlung und der .each()-Funktion für Selektionen

Will man Funktionalitäten, die man in Javascript/jQuery-Projekten erfolgreich entwickelt hat, wiederverwenden, muss man modular arbeiten und seine Funktionen in übergeordneten Einheiten kapseln. Wie einige meiner Leser wissen, versuche ich diese Anforderungen u.a. über die Etablierung abstrakter Control-Objects [CtrlOs] und definierte “Methoden” solcher Objekte umzusetzen.

Einerseits können Konstruktorfunktionen für abstrakte CtrlOs über ihren “prototype” zusammenhängende funktionale Verfahren im Sinne einer reinen Funktionalklasse bündeln.
Anderseits repräsentieren CtrlOs in meinen Web-Projekten bei Bedarf aber auch zusammenhängende Web-Bereiche sowie deren konkrete HTML-Objekte. Sie bündeln dann alle erforderlichen Methoden zum Umgang mit Ereignissen auf diesen Objekten. CtrlOs abstrahieren dabei soweit als möglich von der konkreten HTML-Ausprägung und führen zu einer allgemeineren Repräsentation bestimmter HTML-Schlüssel-Objekte im JS-Code – wenn nötig über die Definition weiterer geeigneter Unter-Objekte des CtrlOs. Wiederverwendbarkeit reduziert sich dann meist auf ein geeignetes Matching der IDs von Schlüsselelementen sowie ein Matching von Selektionskriterien für einen neuen Anwendungskontext (sprich für eine andere Webseite).

Beispiele:
Für die Steuerung einer Bildgalerie auf unterschiedlichen Webseiten ist z.B. lediglich der übergeordnete Container für die Bilder zu bestimmen und es sind geeignete Kriterien zur jQuery-Selektion der beinhalteten Bilder über Parameter zu definieren. Und schon können die meisten vordefinierten Methoden des CtrlOs wieder zum Einsatz kommen. Ein anderes Beispiel würde etwa die Kontrolle der Ajax-Interaktionen eines normierten Form-Elements mit einem PHP-Server zur Durchführung und Statusüberwachung eines Dateiuploads bieten. Auch hier lassen sich alle Funktionalitäten in Methoden eines relativ abstrakten und wiederverwendbaren CtrlOs bündeln.

Unter “Methoden” verstehe ich dabei allgemeine Funktionsobjekte, die als Elemente der fundamentalen “prototype”-Eigenschaft der Konstruktor-Funktionen für das CtrlO und ggf. der Konstruktorfunktionen für Sub-Objekte des CtrlO definiert werden. (In Javascript stellen Funktionen Objekte dar.)

Ziel ist es also, die gesamte Event-Behandlung und auch andere Aufgaben vollständig über “Methoden” eines für die jeweilige Aufgabe “fachlich” zuständigen CtrlOs und eventueller passend definierter Unterobjekte abzuwickeln. Das CtrlO entspricht somit einem (Singleton-) Container, der die Funktionalität zur Abwicklung aller auftretenden Ereignisse und Aufgaben in geordneter und wiederverwendbarer Weise kapselt. (Über den “prototype”-Mechanismus lässt sich dann natürlich auch eine “Vererbung” in komplexeren Projekten realisieren.)

Nun ist jQuery ursprünglich nicht auf eine solche Logik ausgerichtet; es sind daher ein paar Klimmzüge erforderlich, um HTML-Objekte einer Webseite mit Methoden abstrakter JS-Objekte zu koppeln. Vor allem der this-Operator und sein unter jQuery oftmals wechselnder Kontext führen dabei immer wieder zu Missverständnissen und Kapriolen.

So lenken jQuery’s Event-Behandlungs-Funktionen, die für jQuery-Objekte (Selektionen) ausgeführt werden, den this-Kontext in den aufgerufenen Callback-Funktionen auf das jeweilige HTML-Element um. Sind die Callbacks nun Methoden eines CtrlO-Objekts, so passt dies meist nicht zu der Erwartung, die den Entwickler beim Methoden-Design geleitet haben dürfte – nämlich, dass this in der Methode auf das CtrlO-Objekt selbst verweist.

Ich werfe daher zunächst einen Blick auf den $.proxy()-Mechanismus, der einem unter jQuery zumindest im Fall des Event-Handlings hilft, die erforderlichen Aufgaben an CtrlO-Methoden zu delegieren und den Kontext des this-Operators gezielt auf das CtrlO selbst zu setzen.

Danach wende ich mich einer Frage zu, mit der man es auch immer wieder zu tun bekommt:

Wenn man innerhalb einer CtrlO-Methode über eine Selektion von HTML-Objekten mittels der jQuery-Objekt-Funktion $(..).each( …) iteriert – wie delegiert man dann die anstehenden Aufgaben zur Behandlung der selektierten HTML-Objekte an eine (andere) geeignete “Methode” des abstrakten CtrlOs?
Und wie nutzt man dabei den this-Operator, so dass wir einerseits eine Referenz auf das CtrlO selbst und andererseits aber auch eine Referenz auf das aktuell zu behandelnde HTML-Objekt erhalten?

Wir werden sehen, dass sich der Aufruf der gewünschten CtrlO-Methode nicht ganz so bequem wie im Falle der Event-Behandlung über den $.proxy()-Mechanismus bewerkstelligen lässt; wir werden aber auch sehen, dass ein solcher Aufruf über einen kleinen Trick durchaus auf einfache Weise und ohne Informationsverlust möglich ist.

CtrlOs und Einsatz des $.proxy()-Verfahrens zur Delegation der Event-Behandlung an Objekt-Methoden bei gleichzeitiger Kontrolle des this-Operators

Das Abfangen von Events und die anschließende Delegation der funktionalen Bearbeitung an Methoden eines CtrlOs kann man relativ bequem über den $.proxy()-Mechanismus erledigen. Man registriert dazu im Rahmen der Definition der Event-Behandlung mittels geeigneter jQuery-Objekt-Funktionen (wie etwa “click()” ) die Funktion $.proxy() als primären Callback der Event-Behandlungs-Funktion – also z.B. click( $.proxy(), …). Der Funktion $.proxy() selbst wird dann einerseits eine Kontext-Referenz und eine Methode des CtrlO zur Event-Behandlung als Callback übergeben. Diese Verschachtelung von Callbacks hört sich kompliziert an – sie ist faktisch aber sehr bequem. Der Ansatz führt typischerweise zu Code-Abschnitten der unten angegebenen Art.

Voraussetzungen: GOC (“Global Object Controller”) definiert im Beispiel ein globales Objekt; um ein Zumüllen des globalen Kontextes zu vermeiden, werden die benötigten CtrlOs im GOC erzeugt. Wir nehmen für unser Beispiel vereinfachend an, dass die IDs benötigter HTML-Elemente über irgendwelche Parameter ermittelbar sind.


GOC = new Constr_GOC(); 

function Constr_GOC() {
    ....	
    this["CtrlO_Form"] = new Constr_CtrlO_Form( ...parameterS ...); 
    ....
    this["CtrlO_Form"].register_form_events(); 
    ...
}

//  Constructor of a CtrlO for a certain form-Tag  
// -----------------------------------------------
function Constr_CtrlO_Form(... parameters ...) {
    ....
    // gather or define concrete IDs of relevant HTML-objects 
    // by some appropriate evaluation methods
  
    this.id_form     = "#" + " ... (evaluation of parameters) ..."; 	
    this.id_rad_stat = "#" + ...; 	
    this.id_rad_heur = "#" + ...;
    this.id_but_chk  = "#" + ...;
    .....
    ....
}

// Proxy solution to register event handlers for HTML-elements   
// -----------------------------------------------------------
// The behaviour of the HTML-elements shall be controlled by CtrlO-methods 

Constr_CtrlO_Form.prototype.register_form_events = function() {
	
	console.log("From Form - registering event handlers for some key elements of the form");
		
	// Click on any of the radio buttons
	// ----------------------------------
       	$(this.id_rad_stat).click(
       	    jQuery.proxy(this, 'radio_click')
      	);
        $(this.id_rad_heur).click(
            jQuery.proxy(this, 'radio_click')
        );
        $(this.id_rad_both).click(
            jQuery.proxy(this, 'radio_click')
        );
			
	// Button_Click 
	// ----------------
	$(this.id_but_chk).click(
	    jQuery.proxy(this, 'click_event')
	); 
		
	// Submit Event 
	// ------------
	$(this.id_form).submit(
	    jQuery.proxy(this, 'submit_ajax_chkfiles')
	); 
			
};	

// 
Methods of the CtrlO 
// ------------------------------- 
Constr_CtrlO_Form.prototype.radio_click = function(e) {
    .....
    e.preventDefault(); 
    // get the JS-pointer to the HTML-Object for which the event e was triggered   	
    var pointerToHtmlObject = e.target;  	
    ....
    // var attribute = $(pointerToHtmlObject).attr(.....); 
    ....	
}
Constr_CtrlO_Form.prototype.click_event = function(e) {
    ....
}
Constr_CtrlO_Form.prototype.submit_ajax_chkfiles = function(e) {
    ...
}

 
Zuerst werden die IDs relevanter HTML-Schlüssel-Objekte anhand verfahrensspezifischer Kriterien identifiziert. Dann werden Event-Listener und -Handler für diese HTML-Objekte über geeignete jQuery-Funktionen registriert. Die Callbacks ‘radio_click’, ‘click_event’, ‘submit_ajax_chkfiles’ entsprechen im Beispiel Methoden, die über den prototype der Konstruktorfunktion für das instanziierte CtrlO bereitgestellt werden. Der this-Parameter, der an die $.proxy()-Funktion übergeben wurde, weist dabei auf das Objekt “CtrlO_Form” – dieses wiederum ist ein Element des GOC.

Der Aufruf von Callbacks als Event-Handler über jQuery-Objekt-Funktionen wie “.click()” führt dazu, dass der this-Operator innerhalb der im Eventfall ausgelösten Funktion auf das HTML-Objekt (!) verweist. Dies gilt auch, wenn der Callback eine Objektmethode sein sollte. jQuery setzt intern den Kontext für den this-Operator in expliziter Weise! Das führt regelmäßig zur Problemen, da ein Entwickler bei der Ausformulierung von Methoden als Teil des “prototype” eines Konstruktors in der Regel erwartet, dass this auf das (später) erzeugte Objekt zeigen wird.

Das Zwischenschalten der $.proxy()-Funktion sorgt im obigen Beispiel deshalb dafür, dass die jeweils genannte Methode zur Eventbehandlung aufgerufen wird und dass der this-Operator innerhalb der aufgerufenen Methode (wie erwartet) auf das CtrlO und nicht auf das HTML-Objekt zeigt, für das der Event ausgelöst wurde.

Wie aber erhält man nun in der CtrlO-Methode einen Zeiger auf das HTML-Object? Nun dies ist über das Event-Objekt von JS möglich, dass der Methode über den proxy()-Mechanismus automatisch übergeben wird:

Constr_CtrlO_Form.prototype.radio_click = function(e) {...}

e.target liefert dann den gewünschten Zeiger auf das HTML-Objekt. Dieser Zeiger lässt sich dann wiederum für jQuery-Selektionen und zugehörige Funktionen einsetzen.

Der $.proxy()-Mechanismus erlaubt so die vollständige Übergabe der Event-Behandlung an Methoden abstrakter Objekte. Das Verfahren lässt sich im Gegensatz zum Beispiel natürlich auch auf den Aufruf der Methoden anderer Objekte als des aktuell aktiven Objekts verallgemeinern.

jQuery’s .each()-Funktion und der this-Operator

Das obige Beispiel hat verdeutlicht, dass die Kontrolle über den this-Operator für die Delegation der Eventbehandlung an abstrakte Objekt-Methoden wichtig ist.

Nun delegiert auch die Funktion .each() eines jQuery-Objekts (sprich einer Selektion), die Durchführung von Aufgaben im Zusammenhang mit jedem der identifizierten HTML-Objekten an eine Funktion, die als Callback mit vordefinierten Parametern – nämlich (index, element) – festgelegt werden muss (s. https://api.jquery.com/each/). Wie lenkt man die Aufgabendurchführung aber statt dessen auf eine passende Methode eines CtrlOs um? Und wohin weist der this-Operator innerhalb der zu definierenden Callback-Funktion?

Betrachten wir zunächst, wie die .each()-Funktion im Zusammenhang mit Selektionen praktisch zum Einsatz kommt. Wir nehmen im nachfolgenden Beispiel an, dass bestimmte IMG-Tags einer Bilder-Galerie durch die CSS-Klasse “enlarge” gekennzeichnet sein sollen. Die Verwendung von .each() sieht dann im einfachsten Fall etwa so
aus:


// iterate over all (IMG-) tags with css class="enlarge"
// -----------------------------------------------------
$(".enlarge").each(
	function () {
		// Note: this in this function points to the HTML object, here an identified IMG tag 
		.....
		// Code to perform actions, e.g. on the HTML object
		// console.log("id des HTML-Elements : " + $(this).attr("id"); 
		..... 	
	}	
);

 
Wir führen eine Selektion durch und definieren innerhalb von each() eine Callback-Funktion, die dann bestimmte Dinge im Zusammenhang mit jedem der ermittelten HTML-Objekte des DOM-Baums der Webseite durchführt. Z.B. könnten wir dessen Attribute oder CSS-Eigenschaften verändern oder aber Event-Listener implementieren, etc..

Wichtig ist, sich zu vergegenwärtigen, dass der this-Operator innerhalb der Funktion “function() {…}”, die pro Element der Selektion ausgeführt werden soll, auf das aktuell betroffene HTML-Element verweist! Gemeint ist hier tatsächlich die JS-Referenz auf den DOM-Knoten – und kein JQuery-Objekt einer Selektion. Dieser Unterschied ist wichtig : Will man nämlich jQuery-Funktionen, die für Selektionen (also ein jQuery-Objekt) definiert sind, anwenden, so ist zuvor $(this) zu bilden !

“function() {…}” muss bestimmte formale Voraussetzungen bzgl. der Parameter erfüllen. Deshalb kann man “function() {…}” nicht so ohne weiteres durch Callback-Verweise auf beliebige Funktionsobjekte des JS-Codes ersetzen. Es ergibt sich daher die Frage:

Wie kann man über die innerhalb von each() definierten Callback-Funktion “function() {…}” Methoden eines CtrlOs aufrufen und die Referenz auf den DOM-Knoten dabei weiterverwenden?

Nehmen wir mal an, unser Code-Schnipsel von oben sei Teil einer Methode eines CtrlOs, welches wiederum als Element eines globalen Objects GOC definiert sei. Innerhalb des CtrlO-Konstruktors werde im Beispiel eine Initialisierungs-Methode “init_img_control()” aufgerufen; sie möge die Selektion von (IMG-) Tags anhand der CSS-Klase “enlarge” durchführen und pro (IMG-) Tag Aktionen zur späteren Bildkontrolle ausführen. Diese Aktionen seien in der Methode “set_img_nr()” des CtrlO definiert:


GOC = new Constr_GOC(); 

function Constr_GOC() {

    ....
    this.CtrlO_Manipulate_Pics = new this['Constr_CtrlO_Manipulate_Pics']( ...parameter, ...);
    ....
    ....
    // Konstruktor eines CtrlO - this refers to the GOC 
    // -----------------------
    this.Constr_CtrlO_Manipulate_Pics = function(.. parameter, ...) {
        ....
        var numOfImgs = 0; 
        ....
        // "this" refers here to the CtrlO which is created via the present constructor 		
        ...
        this.init_img_control();
        .... 
    };

    // CtrlO-Methode init_img_control()
    // -----------------------------------	
    this['Constr_CtrlO_Manipulate_Pics'].prototype.init_img_control = function() {
	
        // this refers to the CtrlO
        ....
        $(".enlarge").each(
            function () {
                // Note: this points here to an HTML object, 
                // an identified img tag with the CSS class "enlarge" 
                ....
                // Code to perform actions, e.g. on the HTML object

                // console.log("id des HTML-Elements : " + $(this).attr("id"); 

                // ... We WANT TO CALL set_img_nr() - BUT HOW TO DO IT PROPERLY ??? ....
                .....
            }	
        );
        .....
        .....
    };

    // CtrlO-Methode set_img_nr() - to be used for each IMG-tag
    // -----------------------------------------------------------	
    this['Constr_CtrlO_Manipulate_Pics'].prototype.set_img_nr = function(ref_to_img_tag) {
        ....
        var img_nr = 0; 
        // Count the number of identified IMG tags
        this.numOfImgs++; 
        img_nr = this.numOfImgs; 
        ... 
        $(ref_to_img_tag).attr("imgNum", img_nr.toString() );  
        ....
    };

}

 
Wir haben hier nebenbei für Interessierte noch eine weitere zusätzliche Kapselung im GOC vorgeführt:

Die Konstruktor-Funktion für das CtrlO kann selbst als Element des globalen GOC definiert werden. Das GOC-Objekt erzeugt dann das CtrlO als weiteres Unterobjekt durch expliziten Verweis auf das als Konstruktor zu verwendende Funktionsobjekt (this[“Name_des_Funktionsobjekts“]) nach dem new-Operator. Dass sowas tatsächlich geht, ist hier aber nur eine Randnotiz (siehe hierzu den Blog-Artikel Javascript: Einsatz des new-Operators mit Variablen und dynamischer Vorgabe der Konstruktorfunktion).

Im Beispiel wird per “$(..).each()”-Funktion über selektierten Objekte iteriert; wir möchten dabei z.B. gerne jedes der per $(“.enlarge”) ermittelten <IMG>-Tags um eine neues künstliches Attribut ergänzen; so könnten wir in diesem Attribut etwa eine Nummer hinterlegen, die wir später zur Identifikation des Bildes nutzen. Die Attribut-Ergänzung soll im Beispiel über die Methode “set_img_nr()” des CtrlOs GOC[“CtrlO_Manipulate_Pics”] geschehen.

Das Dumme ist nun, dass this innerhalb von “function() {…}” auf das aktuell identifizierte HTML-Element – also ein <IMG class=”enlarge” … >-Tag – verweist. Wir müssen das CtrlO daher anders referenzieren. Aus Gründen der besseren Wiederverwendbarkeit wollen wir zur Identifikation des CtrlOs aber keinen Bezug auf eine explizite Bezeichnung von Unterobjekten des GOC heranziehen; wir woll vielmehr auf ein geeignetes “this” zurückgreifen, das unmittelbar auf das CtrlO verweist. Andererseits soll aber die CtrlO-Methode “set_img_nr()” auch einen Verweis auf den aktuellen HTML-Knoten (also das IMG-Tag) als Parameter erhalten.

Ein wenig Nachdenken führt zu folgender Lösung für die Methode init_img_control() :


    this['Constr_CtrlO_Manipulate_Pics'].prototype.init_img_control = function() {
	
        // this still refers to the CtrlO
        ....
        ....
        // Use a helper variable to store the reference to the CtrlO

       var this_CtrlO = this;  // "this_CtrlO" points to the CtrlO (=GOC["CtrlO_Manipulate_Pics"])
                               // we could have called the helper variable also "that"
        ....
        $(".enlarge").each(
            function () {
                // Note: "this" now points to the HTML object, here an identified img tag 
                // Note: the variable "this_CtrlO" is found by implicit search in nested JS-functions 
                ....				
                .....
                // Code to perform actions, e.g. on the HTML object
                // console.log("id des HTML-Elements : " + $(this).attr("id");
                .....
                // we refer to the appropriate method of the CtrlO via the helper variable  
                // and provide the reference to the <IMG> tag (=this) as a parameter 

                this_CtrlO.set_img_nr(this); 
                ....
            }	
        );
        .....	
    };

 
Alles gut!
Der Profi erkennt sofort den klassischen Trick: Wir merken uns vor der Durchführung der jQuery-Selektion die Referenz auf das CtrlO in einer lokalen Hilfs-Variablen “this_CtrlO”. Da wir uns vor der Selektion ja noch im Kontext
einer gewöhnlichen Objekt-Methode befinden, verweist “this” dort ja noch auf das übergeordnete Objekt.

In der Callback-Funktion “function() {…}”, die innerhalb von .each() definiert wird, rufen wir dann die gewünschte Methode des CtrlO explizit unter Nutzung der Objekt-Referenz “this_CtrlO.Methodenname” auf. Die Variable wird im Lösungsansatz durch implizite Suche im Kontext der umgebenden, kapselnden Funktion init_img_control() gefunden.

Innerhalb der Callback-Funktion “function() {…}” verweist this nun jedoch auf das aktuell zu behandelnde HTML-Objekt der Selektion. Diese Referenz auf das HTML-Objekt können wir deshalb als Parameter (this) an die Objektmethode übergeben. Das zu bearbeitende HTML-Objekt kann danach auch in der CtrlO-Methode eindeutig identifiziert und bearbeitet werden.

Hinweis: Das zwischenzeitliche Speichern der this-Referenz in einer anderen Variablen entspricht in anderen Lehrbuchbeispielen oft dem Einsatz einer Hilfsvariablen “that”.

Unschöne impliziter Variablen-Suche

Wir haben uns mit dem obigen Lösungsansatz allerdings ein unschönes Thema für die Code-Pflege eingehandelt, das mit dem Hoisting von Variablen in Funktionen zu tun hat und in komplexeren Codes bei Unachtsamkeit zu Problemen führen kann: Die Variable “this_CtrlO” darf innerhalb von “function() {…}” an keiner Stelle über var-Statements redefiniert werden – auch nicht nach dem Statement “this_CtrlO.set_img_nr(this);”. Man erkennt leider nicht zwingend, dass es sich eigentlich um einen Parameter handelt, der nach unten durchgereicht wird. Das würde man gerne etwas expliziter sehen.

Man könnte diesem Thema einerseits dadurch ausweichen, dass man die Werte in Variablen speichert, die über das GOC explizit adressiert werden. Das würde unsere Kapselung aber auch wieder durchbrechen. Besser erscheint eine Übergabe der Referenz als Parameter in den each()-Bereich. Ein anderer Ansatz besteht deshalb in der expliziten Übergabe an eine sog. “immediate function” – das ist zwar auch nicht optimal, aber schon ein wenig besser. Man erkennt die Variable wenigstens als Parameter. Ganz nach unten durchreichen kann man die Referenz auf das CtrlO in diesem Falle halt leider nicht.


    this['Constr_CtrlO_Manipulate_Pics'].prototype.init_img_control = function() {

        // this still refers to the CtrlO
        ....
        ....
        // Use a helper variable to store the reference to the CtrlO

        var this_CtrlO = this;  // "this_CtrlO" points to the CtrlO (=GOC["CtrlO_Manipulate_Pics"])
                                // we could have called the helper variable also "that"
        ....
        ( function (pointer_CtrlO) {	
              $(".enlarge").each(
                  function () {
                      // Note: "this" now points to the HTML object, here an identified img tag 
	
                      // get external reference to the CtrlO
                      var CtrlO = pointer_CtrlO; // best at the top of the function code 
                      ....				
                      .....
                      CtrlO.set_img_nr(this); 
                      ....
                  }	
              );
          }
        )(this_CtrlO);
        .....	
    };

 
Man beachte: Die “immediate function” wird im Kontext des vorliegenden Beispiels in der Initialisierungssfunktion “init_img_control” nur genau einmal ausgeführt.

Fazit

Über die Callback-Funktion, die innerhalb der “.each()”-Funktion für Selektionen zu definieren ist, lassen sich auch Methoden eines abstrakten Control Objects [CtrlO] zur Behandlung der selektierten HTML-Objekte aufrufen. Das gilt selbst dann, wenn die Selektion $(..).each( ..) innerhalb einer anderen
Methode desselben CtrlOs vorgenommen wird. Dem Kontextwechsel des this-Operators in den verschiedenen Code-Bereichen der Funktionen beugt man durch rechtzeitige Zwischenspeicherung in einer geeigneten lokalen Hilfsvariable im Scope der kapselnden Methode vor. Die this-Referenz auf das selektierte, aktuell zu behandelnde HTML-Objekt kann man den CtrlO-Methoden als Parameter übergeben. Damit können Aufgaben, die im Rahmen von .each() durchzuführen sind, vollständig an geeignete Methoden von Control Objects delegiert werden.

Viel Spaß weiterhin mit jQuery, Javascript und der Kapselung von Funktionalität in Objekt-Methoden.

Javascript: Einsatz des new-Operators mit Variablen und dynamischer Vorgabe der Konstruktorfunktion

In PHP gibt es die Möglichkeit, den “new” Operator mit einer Variablen zu kombinieren. Dies entspricht einer indirekten Adressierung des Konstruktors. Beispiel :

$ClassName = "Class_Test"; 
$Obj = new $ClassName( param1, param2, ...); 

 
Probiert man etwas Ähnliches unter Javascript, so erleidet man Schiffbruch. Der String-Wert einer Variablen wird vom “new” Operator nicht als Bezeichnung einer Konstruktorfunktion interpretiert.

Sieht man typische Webseiten oder auch Bücher nach der Verwendung des “new”-Operators durch, so wird nach dem “new”-Operator fast durchgehend die Benutzung eines Literals beschrieben, das dem Namen der definierten Funktion entspricht:

function meterpreter() {
	this.alpha = 5; 
}      
var Obj = new meterpreter(); 
echo "value of property alpha = " +  Obj.alpha;    

 
Die Verwendung eines Literals scheint eine indirekte, dynamische Adressierung auszuschließen.

Nun passiert es einem aber auch unter Javascript, dass man ein Objekt gerne aus einem dynamischen Kontext heraus erzeugen möchte. Dann will man den “new”-Operator mit einer Variablen kombinieren, die dynamisch ermittelt wurde, und deren Wert irgendwie die Konstruktorfunktion referenzieren soll. Ein Beispiel:

Man möchte beim Startup einer Webseite eine Reihe von (Singleton-) Objekten erzeugen. Die Namen der zugehörigen
Konstruktorfunktionen seien in einem Array gespeichert. Ein Loop über das Array könnte die Objekterzeugung durchführen, wenn der “new”-Operator mit dem jeweiligen Array-Eintrags verbunden werden könnte und dadurch dann indirekt auch der jeweils benötigte der Konstruktor aufgerufen werden würde. Wie also nutzt man den “new”-Operator zusammen mit Variablen unter JS?

Hier hilft es, sich daran zu erinnern, dass definierte JS-Funktionen Objekten entsprechen, die sich Variablen zuordnen lassen. Nehmen wir in Anlehnung an das obere Beispiel an, wir hätten ein globales Objekt definiert, dass wir GOC (Global Object Controller) nennen wollen und das ein Array mit Namen für potentielle (Singleton-) Objekte beinhaltet. Nehmen wir ferner an, dass wir zugehörige Konstruktorfunktionen bestimmten Variablen des GOC mit normierten Namen zugeordnet hätten:

this.GOC = new Global_Object_Controller();  
this.GOC.createObjects(); // hier konstruieren wie Objekte als Elemente des GOC

function Global_Object_Controller() {
	this.ay_ctrl_obj_names  = new Array('Gallery', 'Ajax', ....);
	this.num_ctrl_objs = this.ay_obj_names.length;

	// Konstruktor Definition
	// ----------------------- 
	this.Constr_Gallery = function( param ) {
	 	.....	
		this.alpha = param + 1; 
		.....
		.....
 	};

	// Method definition of Constr_Gallery 
	// Wir verwenden zur Abwechslung mal die an Arrays angelehnte Notation ...
	this['Constr_Gallery'].prototype.do_something = function() {
		...
		this.obj_num += 10; 
		...
	};  
	.....
	..... 	

	// Konstruktor Definition
	// ----------------------- 
	this.Constr_Ajax = function( param ) {
	 	.....	
		this.obj_num = param + 1; 
		.....
		.....
 	};
	....
	....
}

 
Dann können wir z.B. über eine Methode tatsächlich Objekte dynamisch erzeugen lassen – und zwar z.B. über folgenden simplen Trick:

Global_Object_Controller.prototype.createObjects = function() {
	
	var ctrl_obj_name = ''; 
	var constr_name = ''; 
	....
	for (i=0; i<this.num_ctrl_objs; 
i++) {
		...
		ctrl_obj_name = "Obj_" + this.ay_ctrl_obj_names[i]; 
 		constr_name   = "Constr_" + this.ay_ctrl_obj_names[i];

		// Indirekter Aufruf des Konstruktors über eine Variable (hier des GOC)  
		// *******************************************************************
		this[ctrl_obj_name]  = new this[constr_name](i); 
		// ******************************************************************** 		
		// ist identisch mit 
		// window.GOC[ctrl_obj_name] = new window.GOC[constr_name](i); 		
	
		// console.log("Objekt " + ctrl_obj_name + " im GOC kreiert");  
  		....
	}	
	.....
	// console.log("Obj_Gallery.obj_num = " + this["Obj_Gallery"].obj_num ); // value=1 
	this["Obj_Gallery"].do_something(); 
	// console.log("Obj_Gallery.obj_num = " + this["Obj_Gallery"].obj_num ); // value=11  
	// console.log("Obj_Ajax.obj_num = "    + this["Obj_Ajax"].obj_num );    // value=2  
	
	.....
};   

 
Das funktioniert! Man erkennt, dass in unserem sehr simplen Beispiel die gewünschten Objekte über Variablenaufrufe als Elemente des GOC-Objekts erzeugt werden.

Man kann den “new”-Operator also tatsächlich mit Variablen (irgendwelcher Objekte oder von “window”) kombinieren, wenn diesen Variablen bereits sinnvolle Konstruktorfunktionen zugeordnet wurden. Das erinnert an den Callback-Mechanismus – hier allerdings für den “new”-Operator.

Die indirekte Adressierung erfolgt im Gegensatz zu PHP dadurch, dass man den “Namen” der Variablen im Zuge des Algorithmus dynamisch festlegt und darüber dann auf den Konstruktor als das Funktions-Objekt referenziert. Das erfordert zwar ein etwas anderes Vorgehen, schränkt einen aber im Vergleich zu PHP in der Zielerreichung einer dynamischen und indirekten Konstruktor-Referenzierung nicht ein.

Dadurch wird man bzgl. der Objektgenerierung sehr viel flexibler, als wenn man sich immer mit Literalen herumschlagen müsste.

Off Topic:

Das Beispiel zeigt übrigens, dass Konstruktorfunktionen nicht immer zwingend im globalen Kontext (window) platziert werden müssen. Sie können auch jederzeit Variablen eines bereits definierten übergeordneten Objekts (oder seines Konstruktors) zugeordnet werden. Dies eröffnet in bestimmten Fällen weitere Möglichkeiten der Kapselung von Code.
In der Praxis muss man sich eine Zuordnung von Konstruktor-Funktionen zu Variablen aber gut überlegen. Schon aus Gründen der Codeverwaltung wird man Konstruktorfunktionen für wichtige Objekt in separaten, eigenen Dateien aufbewahren. Beim Laden der Dateien werden die Funktionen dann typischerweise in den globalen Kontext platziert.
Zudem gilt: In komplexen Codes, die z.B. mit Eclipse oder anderen Tools gewartet werden, ist die Zuordnung von Konstruktorfunktionen zu Variablen eines anderen Konstruktors möglicherweise mit dem Nachteil verbunden, dass Code-Outliner diese Sub-Konstruktoren, zugehörige Variable und Funktionen ggf. nicht mehr als eigene Objekte ausweisen – sie sind dann einer einfachen Navigation im Code über den Outliner nicht mehr zugänglich.

Will man die Konstruktoren deshalb aus guten Gründen unabhängig von anderen Objektbeschreibungen definieren, so ist das Codebeispiel leicht zu modifizieren: Man definiert die Konstruktorfunktionen global und nicht im GOC. Und ruft sie dann im GOC und dessen Loop wie folgt auf:

this[ctrl_obj_name] = new window[constr_name](i);

Viel Spaß weiterhin mit Javascript!

nPA, eID und Open eCard unter Linux/Opensuse

Vor kurzem musste ich mich mit der Frage auseinandersetzen, wie ich die eID des neuen Personalausweises [nPA] unter Linux nutzen kann. Da ich in Bayern wohne, war ich u.a. daran interessiert, Zugang zur sog. BayernID und einem damit verbundenen “Bürgerkonto” zu bekommen, das von der Landesregierung über ein Infrastruktur der AKDB zentral bereitgestellt wird. Dieses Konto kann in allen sog. “Bürgerservice”-Portalen der in Bayern an die AKDB angeschlossenen Kommunen verwendet werden kann. Der im Alltag spürbare Nutzen ist aufgrund des noch sehr begrenzten Angebots an transaktionalen Online-Diensten, in denen der eID-Einsatz die Schriftformerfordernis abdeckt, allerdings noch relativ bescheiden. Aber das ist eine andere Diskussion …

Welche Erfahrung macht man nun, wenn man sich als Linux-Nutzer registrieren will? Leider wird man umgehend frustriert und enttäuscht:

Linux-User werden von Hauptseiten der Portal- und Dienstebetreiber schlichtweg ignoriert; sie tauchen in den Anleitungen zum Einsatz der eID flächendeckend nicht auf. Grundsätzlich wird die AusweisApp2 als Voraussetzung der eID-Nutzung genannt. Diese Anwendung steht jedoch nur für MS Windows und Apple OS X zur Verfügung. Der übliche Verweis auf die Website “https://www.ausweisapp.bund.de/startseite/” für weitere Informationen lässt den Linux-User dann jedoch im Regen stehen. Nach mühsamer Suche erfährt man unter FAQs bzw. in einem Forum lapidar, dass Governikus angeblich auf die am häufigsten eingesetzten Betriebssysteme gezielt habe.

Aufregen nutzt hier nichts – die eigentlich relevante Frage, wie hoch der Prozentsatz der E-Government-Willigen pro Betriebssystem ist und was das dann in absoluten Zahlen bedeutet, wurde wohl nicht als relevant angesehen. Im Ergebnis ist festzuhalten, dass die Möglichkeit zur Plattformneutralität trotz Opensource-Politik von Governikus nicht genutzt hat. Zudem wurde leider die Chance vertan, gerade bei denjenigen Staatsbürgern Pluspunkte und Vertrauen zu gewinnen, die E-Government befürworten und dabei den deutschen Datenschutz womöglich ernster nehmen als ein bestimmter Betriebssystemhersteller, der für seine Nutzungsbedingungen und den umfassenden Zugriff auf personenbezogene Daten im Rahmen von Standardinstallation kürzlich von einer deutschen Verbraucherzentrale heftig kritisiert wurde.
http://www.sueddeutsche.de/digital/klage-windows-verbraucherzentrale-geht-gegen-microsoft-vor-1.2886619

Open eCard

Welche Alternative hat man nun als Linux-User, der E-Government-Anwendungen in Kombination mit der eID nutzen will? Governikus verweist auf das “persoapp”-Projekt. Das ist jedoch nicht fertig – unter Opensuse Leap mit Java OpenJDK 1.8 und KDE 5 lief die Test-Anwendung jedenfalls (noch) nicht. Auch ein Java-Webstart von des Testseite führte nicht zum Erfolg.

Nach ein wenig Recherche wird man aber dank anderer engagierter Zeitgenossen fündig: Es gibt auch das Open eCard-Projekt – siehe:
https://www.openecard.org/startseite/
Dort kann man sich eine Java Web Start Applikation und bei Interesse den zugehörigen Code herunterladen. Angemerkt sei, dass die Open eCard-Anwendung Anfang 2016 vom BSI in puncto Sicherheit zertifiziert wurde. Siehe hierzu:
http://www.pro-linux.de/news/1/23138/bsi-zertifiziert-offenen-eid-client.html

Voraussetzung der Anwendung ist eine JAVA Run Time Umgebung JRE >= Version 1.7. Hat man ferner einen NFC-fähigen Kartenleser, für den der Hersteller Linux-Treiber bereitstellt (etwa von ReinerSCT), so nimmt die Open eCard-Anwendung den Kartenleser anstandslos wahr und zeigt ggf. an, dass (k)eine RFID-Karte oder eben der
neue Personalausweis eingelegt ist.

eID_opeecard_0
eID_opeecard_01

Unter KDE platziert sich die Anwendung in der Systemkontrollleiste – und wartet im Hintergrund. Ein Klick öffnet unter KDE 4 ein Anwendungsfenster. Das funktioniert unter KDE5 (noch) nicht – das tut aber der späteren Funktionalität in Kombination mit E-Government Web-Anwendungen im Browser keinen Abbruch.

Die Anwendung kommt extrem schlicht daher; es gibt kaum Infos und Einstellmöglichkeiten; das Layout ist maximal schmucklos.

eID_opeecard_02

Wirklich zu werkeln beginnt die Anwendung dann, wenn eine E-Government-Web-Anwendung im Browser – z.B. das AKDB-Konto beim Login – die eID auslesen will. Man wird dann vom Kartenleser wie der Open eCard-Anwendung vorab über die Berechtigung der zugreifenden Instanz informiert und – soweit noch nicht getan – zum Ein-/Auflegen des nPA in das Lesegerät aufgefordert.

eID_opeecard_1

Zudem wird rudimentär und für den Unkundigen etwas kryptisch der Typus der Daten angezeigt, die ausgelesen werden.

eID_opeecard_2

Unter “pseudonym” kann sich nicht jeder was vorstellen. Weiter helfen dem Interessierten Bürger hier Informationen dieser Links:
http://www.die-pseudonym-funktion.de/?navigation=login-per-pseudonym-funktion-des-personalausweises
http://www.personalausweisportal.de/DE/Wirtschaft/Diensteanbieter-werden/Einsatzmoeglichkeiten/Pseudonymer-Zugang/pseudonym_node.html
http://www.die-eid-funktion.de/

Im Falle komplexerer Transaktionen als einem Login werden hier deutlich mehr personenbezogene Daten angezeigt. Z.B. bei der Abfrage der gespeicherten nPA-Daten über eine Website des BMI https://www.buergerserviceportal.de/bund/ausweisapp/bspx_selbstauskunft/index.jsp

eID_opeecard_4

Interessanterweise findet Firefox die genannte Seite des BMI nicht vollständig sicher; das letztlich referenzierte Zertifikat der AKDB) ist aber OK.

eID_opeecard_5

eID_opeecard_6

Ja, ja, … Querverweis zur Anwendung nicht ganz glücklich implementiert. Unter dem Bayernportal läuft die gleiche Auskunftsanwendung dagegen ohne Sicherheitswarnung. Man versuche es selbst unter https://www.buergerserviceportal.de/bayern/freistaat/bspx_selbstauskunft

Nach PIN-Eingabe und dem Auslesevorgang wird die Kontrolle dann wieder an die Web-Anwendung abgegeben. Dankenswerterweise fordert einen “Open eCard” dann auf, den Personalausweis wieder aus dem Kartenleser zu entfernen.

eID_opeecard_3

Ich habe inzwischen mehrere kommunale Verwaltungsportale besucht und immer dann, wenn die Bedienungsanleitungen die AusweisApp2 verlangten, unter Linux auf die Open eCard zurückgegriffen. Konnte bislang kein Problem erkennen.

Fazit

Also – wenn auch nicht mit der AusweisApp2 und ohne Unterstützung von Governikus:

Dank der Leute, die zu Open eCard beigetragen haben, kann auch der Linuxer E-Government Dienste in Deutschland nutzen, die die eID erfordern. Mit BSI Sicherheitszertifizierung. Ich jedenfalls danke dem Open eCard Team für seine Anstrengungen und Verdienste um die Zertifizierung recht herzlich.

Opensuse Leap 42.1 mit KDE 5 – Eindrücke nach 2 Monaten Nutzung – anfänglich viel Schatten, nun ein wenig Licht …

Ich sag’s mal gleich vorweg:

Leap 42.1 gehört aus meiner Sicht nicht zu den Opensuse Versionen, die sich auf einem Desktop-System auf Anhieb problemfrei installieren und danach ohne weiteres auf persönliche Bedürfnisse hin umkonfigurieren lassen.

Diese Aussage bezieht sich vor allem auf die auf DVDs ausgelieferte Version bzw. auf die Download-Version von Anfang Januar 2016. Die war und ist aus meiner Sicht eher ein kleines Desaster und keineswegs Werbung für das Leap-Projekt im Sinne einer stabilen Linux-Version für einen KDE-Desktop.

Viele der Probleme hatten und haben allerdings wohl mit KDE 5 zu tun – und können daher nicht unbedingt Opensuse angelastet werden. Auf meinem Testsystem sind auch OS 13.2 und OS 13.1 installiert und konnten seit geraumer Zeit produktiv und stabil genutzt werden. Die im Januar noch regelmäßig festgestellten Abstürze und Instabilitäten unter OS Leap 42.1 mit KDE5 sind daher nicht auf die Hardware zurückzuführen.

Die Stabilität des gesamten Leap-Systems hat sich inzwischen – nach zahlreichen Paketupdates und auch Repository-Wechseln – allerdings deutlich verbessert. Man kann inzwischen mit Leap 42.1 produktiv arbeiten – Ende Januar hätte ich diesen Satz so noch nicht ausgesprochen. Hier ein erster Erfahrungsbericht.

Produktives Opensuse 13.2 auf jeden Fall weiternutzen und nicht upgraden

Grundsätzlich würde ich jedem, der über einen Wechsel nachdenkt, empfehlen, ein stabil laufendes Opensuse 13.2 auf keinen Fall aufzugeben oder upzugraden. Der von mir selbst regelmäßig gewählte Ansatz, ein neues Opensuse-Release (speziell mit einer grundlegend neuen KDE-Version) zunächst in einer separaten Partition als das aktuell produktive Desktop-System zu installieren und auszuprobieren, erscheint mir gerade im Fall von Leap 42.1 der absolut richtige zu sein. Es gibt immer noch Dinge, die mich ab und zu veranlassen, wieder OS 13.2 zu benutzen.

Neuerungen und der Hybrid-Ansatz unter Leap 42.1

Neuerungen unter Leap 42.1, wie etwa der Mix aus KDE4 und KDE5 sowie der sog. Hybrid-Ansatz als Mischung aus stabilem Unterbau auf Basis des aktuellen SLES 12 Servers und einem relativ aktuellem Desktop, sind bereits in vielen Reviews beschrieben worden. Ich möchte diesbezüglich daher nur auf einige Artikel verweisen:

https://kofler.info/opensuse-leap-42-1/
http://www.heise.de/open/artikel/OpenSuse-Leap-42-1-Der-Nachfolger-von-OpenSuse-2877560.html
http://ordinatechnic.com/distros/distro-reviews/opensuse/opensuse-leap-421-review
https://www.curius.de/blog/13-linux/40-opensuse-leap-42-1-im-test
http://www.unixmen.com/review-opensuse-42-1-leap/
http://www.theregister.co.uk/2015/10/20/opensuse_leap_opensuse_leap_adoption/?page=1
https://www.youtube.com/watch?v=hkf4l6fIMo4

Persönlich überzeugt mich SuSE’s Hybrid-Ansatz noch nicht völlig. Da wir in unserer kleinen Firma Opensuse aber auch als Server-Plattform einsetzen, erhoffe ich mir – nach einer Phase noch zu findender Stabilität auf der Desktop-Seite – doch langfristig einen einheitlicheren Verwaltungsaufwand für Entwicklungssysteme (mit einigen Serverkomponenten) einerseits und für die von uns genutzten reinen Server- und Virtualisierungsplattformen andererseits. Die größten Probleme mit aktuellem System sehe ich eindeutig auf Seiten von KDE 5 – genauer Plasma 5.

Windows-ähnliches Look and Feel von KDE 5 mit Breeze

Persönlich bekam ich anfänglich fast einen Schock als ich das “Breeze Look&Feel” für KDE 5 vor Augen hatte. Das erinnerte mich doch zu stark an MS Oberflächen. Aber inzwischen habe ich mich daran gewöhnt; zumal man sich doch viele Eigenschaften des Plasma5-Desktops so zurecht biegen kann, dass die Ähnlichkeit zu Windows am Ende recht weitgehend verschwindet.

Dennoch vermisse ich ein wenig die Vielzahl von vorgefertigten Designs und Stilen für Bedienelemente, die unter KDE 4 bereits gegeben war. Ich denke aber, das ist nur eine Frage der Zeit …

Höhere Stabilität als OS 13.1/OS 13.2 ? Nein, wegen KDE 5 Plasma nicht wirklich ….

Dass OS Leap 42.1 zumindest in Teilen auf SLES 12 aufsetzt, wurde wegen der zu erwartenden Stabilität in vielen Reviews gelobt. Desktop-seitig kann ich diesen Eindruck jedenfalls nicht teilen.

Dies liegt wie gesagt vor allem an KDE. Ich habe mir nämlich mal die reinen Serveranteile in Form eines LAMP-Test-Servers und eines KVM-Test-Virtualisierunsghosts, auf den ich Guest-Images aus einem Produktivsystem migriert habe, auch mal separat ohne KDE angesehen. Fazit: Der Server-Unterbau – ohne KDE-Desktop – wirkt grundsolide.

Eine Standard-Installation der Desktop-Pakete aus dem heruntergeladenen ISO Image führte bei mir dagegen früher oder später zu größeren Problemen. Besonders zu nennen sind vor allem anfänglich extrem häufig auftretende Instabilitäten des gesamten KDE 5 Plasma-Desktops und Probleme mit den Abmelde- und Shutdown-Funktionalitäten. Die ersten 4 Versuchstage im Januar waren von folgenden Erfahrungen geprägt:

  • Erratische Absturzmeldungen von “konsole”-Fenstern,
  • Freezes des Plasma-Desktops (trotz damals aktuellstem Nvidia-Treiber),
  • Abstürze des neuen Window-Managers “kwin_x11”
  • Ungereimtheiten in der Fensterdarstellung, wenn man als User root Qt5-Anwendungen von der “konsole” aus startete. Es wurde/wird dann nicht erkannt, dass die Applikationen unter einer KDE5-Umgebung laufen. Die resultierende reduzierte Darstellung von Applikationen für den User root (zu kleine Schriften, fehlende Grafiksymbole,etc. …) ist dann mindestens mal ärgerlich. Das ist heute noch so, lässt sich aber umschiffen (s.u.)
  • Erhebliche Probleme mit der Sound-Einrichtung – mit und ohne Pulseaudio.

Siehe auch: https://www.curius.de/blog/13-linux/40-opensuse-leap-42-1-im-test.

Nach vielen Updates sowie Repository-Wechseln für Multimedia-Komponenten sowie zwischenzeitlichem Löschen und Neuaufbau des kompletten “~/.config“-Verzeichnisses hat sich das Bild inzwischen aber verbessert. Zumindest Abstürze und Freezes von Plasma treten inzwischen nur noch äußerst selten auf.

KDE5 bietet aber leider noch nicht die Funktionalität, die unter KDE 4.14 gegeben war; einige liebgewonnene Plasmoide sind z.T. noch nicht für KDE5 umgesetzt oder aber es fehlt noch an gewohnten funktionalen Elementen. Ärgerlich ist zudem der z.T. noch häufig vorkommende Mix aus deutschen, englischen und manchmal auch norwegischen Menü-Einträgen in einigen Schlüssel-Applikationen (wie den KDE PIM-Anwendungen) – wie auch im Bereich der KDE-Systemeinstellungen.

Typisch sind auch kleinere Ungereimtheiten wie etwa die, dass grafische Operationen unter KDE5’s Dolphin extrem langsam werden, wenn k3b gleichzeitig CDs rippt, oder Daten zwischen Partitionen kopiert werden, obwohl das System insgesamt weit von einer Auslastung entfernt ist.

Ärgerlich ist auch, dass Symbole im Systemabschnitt der KDE5-Kontroll-Leiste, die nicht speziell für KDE5 entwickelt wurden, keine Funktionalität aufweisen. Sie reagieren auf keine Art von Mausklick. Beispiele sind etwa das “virt-
manager”-Symbol oder auch das Symbol der “Open eCard-Anwendung” für den neuen Personalausweis.

Aus dem Reich des wirklich Üblen erwies sich u.a. das Plasmoid zur Ordneransicht auf dem Desktop. Ich sage einfach: Finger weg davon, wenn man sich Ärger ersparen will. Und wenn man “Ordneransichten” dennoch nutzen und Änderungen an deren Konfiguration und Inhalt vornehmen will, dann sollte man das am Besten ausgehend von einem Dateimanager mittels Kopieren von Einträgen in “~/Schreibtisch” und danach durch Anpassen der Einträge der zugehörigen Datei über einen Editor tun. Funktioniert deutlich besser und fehlerfreier als entsprechende grafische Operationen am Desktop selbst (Stand Ende Januar; habe mir das bisher nicht mehr genauer angesehen, sondern verankere nur noch Einzel-Icons auf der Plasma-Oberfläche).

Interessanterweise stürzte/stürzt auch Libreoffice 5 manchmal, wenn auch selten, unvermittelt ab. Das muss jedoch nicht zwingend etwas mit KDE5 oder der GTK-Integration zu tun haben.

In jedem Fall gilt:

Ein Update aller System- und KDE-Pakete sollte nach einer DVD-Installation möglichst unverzüglich vorgenommen werden. Am besten lässt man die Standard-Update Repositories von Opensuse schon zum Installationszeitpunkt einbinden. Dennoch kommen ggf. immer mal wieder Abstürze des Plasma-Desktops, von konsole-Fenstern oder des Window-Managers “kwin_x11″ vor. Dann sollte man im Besonderen die Dateien und die Unterverzeichnisse des ~/.config”-Verzeichnisses komplett neu aufbauen lassen. (Z.B. durch Umbenennen dieses Verzeichnisses). Danach verliert man zwar etliche der bislang vorgenommenen Desktop-Einstellungen; der resultierende Aufwand war im Vergleich zum Gewinn an Stabilität aber ein geringer Preis …

SuSE-spezifische HW-Inkompatibilitäten

Durch einen mehrtägigen Mail-Verkehr mit einem Leser dieses Blogs musste ich leider lernen, dass sich Leap 42.1 auf mancher Laptop-HW (etwa auf bestimmten UEFI-basierten Asus-Systemen) nicht zu einer Installation bewegen lässt, bei der am Ende das Grafik-System, Tastatur, WLAN-Komponenten problemfrei laufen würden. Ganz im Gegensatz übrigens zu “Linux Mint 14” – in dessen KDE-Variante. Kein Wunder, dass Linux Mint in Deutschland die populärste Linux-Distribution geworden ist! Dass Opensuse hinsichtlich der HW-Kompatibilität mit Mint nicht mehr mithalten kann, hat mich als alten Opensuse-Anhänger denn doch etwas deprimiert.

Einige Punkte zur Installation und Grundkonfiguration

Kleine Unterschiede zwischen LEAP 42.1-ISO-Images?
Offenbar gibt es geringfügige Unterschiede in publizierten ISO-Images. Die DVD, die der letzten Easy Linux DVD-Ausgabe beigelegt war, unterscheidet sich in Kleinigkeiten von der des ISO-Images, das man von der opensuse.org-Website herunterladen kann – u.a. durch die Voreinstellung zur Größe der Grub BIOS Partition. Besondere Auswirkungen konnte ich nicht feststellen. Ich habe mich aber dennoch gefragt, was da noch alles unterschiedlich sein mag ….

BTRFS als Filesystem?

Ich habe die BTRFS-Diskussion schon seit Opensuse 13.1 ein wenig mitverfolgt. Nach einigen vorläufigen Tests, die ich unter OS 13.2 durchgeführt hatte, sehe ich für meine Systeme keinen einzigen Grund, im Moment von “ext4” wegzugehen.

Ich bin deshalb nicht den Partitionierungsvorschlägen des SuSE-Installers gefolgt, sondern habe die Linux-Partitionen meiner Systeme durchgehend mit dem Filesystem ext4 versehen. Neben dem auch von Herrn Kofler ins Feld geführten Argument, dass BTRFS komplexer als ext4 ist und vom Administrator entsprechend mehr Wissen verlangt, waren für mich folgende Punkte ausschlaggebend:

Bzgl. Snapshots: Ich setze auf den meisten meiner Systeme sowieso LVM (und die zugehörige Snapshot-Funktionalität) ein. Für Snapshots benötige ich BTRFS deshalb nicht.
Bzgl. Performance:
ext4 erscheint mir – zumindest auf SSDs (mit und ohne Raid) – im Schnitt auch für die Systempartition hinreichend schnell zu sein. Für Datenbanksysteme gibt es neben ext4 jedoch deutlich performantere Filesysteme als BTRFS.
Der einzige Serverdienst, der nach meiner Erfahrung wirklich von BTRFS zu profitieren scheint, ist ein NFS- und/oder Samba-Fileserver-Dienst für im Schnitt kleine bis mittlere Dateigrößen.

Aber bildet euch selbst eure Meinung; nachfolgend dazu ein paar Artikelhinweise:

https://kofler.info/opensuse-13-2-ausprobiert/
http://www.unixmen.com/review-ext4-vs-btrfs-vs-xfs/
http://www.phoronix.com/scan.php?page=article&item=linux-40-hdd&num=1
http://www.phoronix.com/scan.php?page=article&item=linux-43-ssd&num=1
https://www.diva-portal.org/smash/get/diva2:822493/FULLTEXT01.pdf
http://www.slideshare.net/fuzzycz/postgresql-on-ext4-xfs-btrfs-and-zfs
http://blog.pgaddict.com/posts/friends-dont-let-friends-use-btrfs-for-oltp
http://www.linux-community.de/Internal/Artikel/Print-Artikel/EasyLinux/2015/01/Butter-bei-dat-Dateisystem
http://www.safepark.dk/in-the-news/btrfsunderopensuseleap421-betterharddiskusage
https://forums.opensuse.org/showthread.php/502082-BtrFS-vs-XFS-vs-Ext4/page6?s=ee63ec62915db3e9d5bbaf9cc8b81799
https://wiki.gentoo.org/wiki/Btrfs
https://en.opensuse.org/openSUSE:Snapper_Tutorial

Extrem langsamer YaST-Partitionierungsassistent während der Installation aus einem ISO-Image

Die Installation auf einem bereits komplexen System mit vielen Partitionen und Raid-Systemen erfordert ggf. eigenhändige Partitionierungsarbeiten. Hier werden diejenigen, die eine Installation über ein DVD-ISO-Image durchführen, ggf. eine Überraschung erleben, wenn mehrere komplexe GPT-basierte Harddisk- und Raid-Konfigurationen mit vielen Partitionen unter LVM ausgelesen und bearbeitet werden müssen. Der ursprüngliche YaST-Partitionierungsassistent, zu dem man während der Installation wechseln kann, reagiert dann unglaublich (!) träge. Ganz im Gegensatz zu einfachen Installationen auf einem Laptop mit nur 2 Standard Partitionen (ohne LVM).

Nach einem umfassenden Rundum-Update der Yast- und anderer Leap-Pakete im Anschluss an die Grundinstallation gibt sich YaST’s “Partitioner” dann allerdings wieder so spritzig, wie man das von Opensuse 13.2 gewohnt war. Das sind halt die typischen Kinderkrankheiten, wie sie bei neuen Opensuse-Releases mit substanziellen Änderungen immer mal wieder auftreten.

Einbinden von Online-Repositiories während der Installation

Das funktioniert wie gehabt. Je nach Netzkonfiguration und Firewall-Einstellungen ist dafür zu sorgen, dass die DHCP-Zuweisung einer IP-Adresse funktioniert und das System durch evtl. Firewalls nach außen zu SuSE-Servern über HTTP Kontakt aufnehmen darf.

Zusätzlicher Dummy-User für Tests der Desktop-Stabilität

Ich empfehle, nach der Installation einen Dummy-
User einzurichten, dessen KDE- und Desktop-Einstellungen man unverändert lässt. Treten mit KDE 5 unerwartete Probleme auf, sollte man erstes immer mal testen, ob auch der Dummy-User darunter leidet – oder ob zwischenzeitliche, user-spezifische Einstellungsveränderungen zu dem Problem geführt haben. Das war bei mir mehrfach der Fall.

Proprietärer Nvidia-Treiber

Der während der Installation automatisch konfigurierte Nouveau-Treiber bringt auf der Nvidia-Karte meines Desktop-Systems mit 3 Schirmen alle Boot-Meldungen (im Framebuffer-Modus) und auch den grafischen Anmeldebildschirm von KDE auf allen 3 angeschlossenen Schirmen zur Ansicht.

Das tut der später installierte proprietäre Nvidia-Treiber nicht mehr – der zeigt Boot-Meldungen nur auf genau einem Konsolen-Schirm (in meinem Fall am DVI-Ausgang der Grafikkarte).

Ein paar Hinweise zur nachträglichen Installation des proprietären Nvidia-Treibers:

  • Schritt 1: Auf Basis des zunächst automatisch installierten Noveau-Treibers das Erscheinen des KDE-Login-Schirms abwarten. In KDE 5 einloggen. Bei mehreren angeschlossenen Schirmen zunächst eine vernünftige Anordnung der Displays herstellen. Da ist u.a. möglich über KFDE’s “Systemeinstellungen >> Hardware >> Anzeige und Monitor”.
  • Schritt 2: Dann den aktuellen Nvidia-Treiber von “nvidia.de” herunterladen. Ausloggen von KDE.
  • Schritt 3: Ctrl-Alt-F1, um zur Haupt-Konsole zu wechseln. Dort als User “root” einloggen. Das Kommando “init 3” absetzen, um das X-Window-System zu stoppen.
  • Schritt 4: Zum Verzeichnis mit dem heruntergeladenen Nvidia-Treiber wechseln. Dann:

    mytux:~ # bash NVidia-Linux-x86_64-352.63.run

    Sich durch die verschiedenen Meldungen durchklicken. Bestätigen der Anlage eines Files unter “/etc/modprobe.d” (!). Danach alles bestätigen => alle Error-Meldungen akzeptieren. Das nvidia-Treibermodul ist und wird wegen der Fehler nicht geladen, wie man über ein “lsmod | grep nvidia” sehen kann. Nun nicht booten; das führt nämlich auch nicht zu einem Laden des nvidia-Kernel-Moduls im nächsten Boot-Prozess.

  • Schritt 5: Statt dessen init 5 => Einloggen => Schirmreihenfolge noch OK => root-terminal => Dolphin starten => hunderte Fehlermeldungen auf der Konsole tapfer ignorieren.
  • Schritt 6: Unter “dolphin” die Datei “/etc/modprobe.de/nvidia-installer-disable-nouveau.conf” suchen und mit kwrite öffnen. Parallel die Datei “/etc/modprobe.de/50-blacklist.conf” öffnen.
  • Schritt 7: Den Inhalt von “/etc/modprobe.de/nvidia-installer-disable-nouveau.conf”, nämlich

    # generated by nvidia-installer
    blacklist nouveau
    options nouveau modeset=0

    mit Hilfe von z.B. kwrite an das Ende der Datei “/etc/modprobe.de/50-blacklist.conf” kopieren. Erst diese Aktion stellt sicher, dass das nouveau-Modul beim nächsten Bootvorgang nicht mehr geladen wird. Die vom Nvidia-Installer bereitgestellte Datei “/etc/modprobe.de/nvidia-installer-disable-nouveau.conf” wird von systemd nicht ausgelesen.

  • Schritt 8: Die modifizierte Datei “/etc/modprobe.de/50-blacklist.conf” speichern und kwrite schließen.
  • Schritt 9: In einem Terminalfenster meldet man sich dann erneut als User root an und setzt den Befehl

    mytux:~ # mkinitrd

    ab. Fehlermeldungen am Ende erneut tapfer ignorieren.

  • Schritt 10: Alle Fenster schließen =>
    Neustart-Button (blau) im Startmenü
  • Schritt 11: Der erneute Boot-Prozess endet dann ggf. in der Hauptkonsole. Falls der KDE-Login-Schirm aufgrund des geladenen langsamen nv-treibers angezeigt werden sollte: Ctrl-Alt-F1 => als User root einloggen => Kommando “init 3”.
  • Schritt 12: Nvidia-Treiber erneut installieren – das sollte nun anstandslos funktionieren.
  • Schritt 13: System neu starten (init 6). Nun sollte der grafische KDE-Login-Schirm erscheinen.

Es ist zudem sinnvoll, eine ggf. vorhandene Mehrfach-Schirm-Kombination in der Datei “/etc/X11/xorg.conf” zu verankern. Das sieht in meinem Fall etwa so aus:

# nvidia-xconfig: X configuration file generated by nvidia-xconfig
# nvidia-xconfig:  version 352.79  (buildmeister@swio-display-x64-rhel04-15)  Wed Jan 13 17:02:24 PST 2016

# nvidia-settings: X configuration file generated by nvidia-settings
# nvidia-settings:  version 352.30  (buildmeister@swio-display-x64-rhel04-18)  Tue Jul 21 19:35:20 PDT 2015

# 14.01.2016: Modified by Ralph Moenchmeyer

Section "ServerLayout"
    Identifier     "Layout0"
    Screen      0  "Screen0" 0 0
    InputDevice    "Keyboard0" "CoreKeyboard"
    InputDevice    "Mouse0" "CorePointer"
    Option         "Xinerama" "0"
EndSection

Section "Files"
EndSection

Section "InputDevice"

    # generated from data in "/etc/sysconfig/mouse"
    Identifier     "Mouse0"
    Driver         "mouse"
    Option         "Protocol" "IMPS/2"
    Option         "Device" "/dev/input/mice"
    Option         "Emulate3Buttons" "yes"
    Option         "ZAxisMapping" "4 5"
EndSection

Section "InputDevice"

    # generated from default
    Identifier     "Keyboard0"
    Driver         "kbd"
EndSection

Section "Monitor"
    Identifier     "Monitor0"
    VendorName     "Unknown"
    ModelName      "Ancor Communications Inc PB248"
    HorizSync       30.0 - 83.0
    VertRefresh     50.0 - 61.0
    Option         "DPMS" "false"
EndSection

Section "Device"

# Addendum RMO https://bugs.kde.org/show_bug.cgi?id=322060
    Identifier     "Device0"
    Driver         "nvidia"
    VendorName     "NVIDIA Corporation"
    BoardName      "GeForce GTX 960"
EndSection

Section "Screen"

# Removed Option "metamodes" "DVI-I-1: 1920x1200_60 +0+0, DVI-D-0: 1920x1200_60 +1920+0"
# Removed Option "MultiGPU" "On"
    Identifier     "Screen0"
    Device         "Device0"
    Monitor        "Monitor0"
    DefaultDepth    24
    Option         "Stereo" "0"
    Option         "nvidiaXineramaInfoOrder" "DFP-0"
    Option         "metamodes" "DP-4: nvidia-auto-select +0+0, DP-0: nvidia-auto-select +2560+0, DVI-I-1: nvidia-auto-select +5120+0"
    Option         "SLI" "Off"
    Option         "MultiGPU" "Off"
    Option         "BaseMosaic" "Off"
    Option         "TripleBuffer" "True"
    SubSection     "Display"
        Depth       24
    EndSubSection
EndSection

 
Die Anweisungen im Screen-Bereich sorgen dafür, dass der Treiber beim Starten des X-Window-Systems die Zuordnung der verschiedenen Schirme zu den Grafikkarten-Ports korrekt umsetzt. Danach erscheint die KDE-Login-Oberfläche schließlich auf allen drei Schirmen.

Achtung: Ich rede hier über ein Desktop-System. Eine Nvidia-Installation auf Laptops mit Optimus-Technologie erfordert andere Schritte. Siehe z.B.:
Bumblebee
auf Laptops mit Opensuse 13.1 / 13.2

Ich habe das dort beschriebene Verfahren allerdings noch nicht mit Leap 42.1 ausprobiert.

Ist der nvidia-Treiber erstmal installiert, so findet man die Konfigurationsanwendung “nvidia-settings” im neuen Startmenü übrigens unter dem Punkt “Einstellungen”.

Im Falle von Mehrschirm-Arbeitsplätzen sollte man abschließend prüfen, dass die Schirmanordnungen und die schirmbezogenen Einstellungen unter “nvidia-settings” mit jenen unter KDE’s “Systemeinstellungen => Anzeige und Monitor” sind. (Anfang Januar war das nämlich nicht unbedingt der Fall).

Sound-Konfiguration – schwierig – unvollständige Anzeigen

Zur leidigen Sound-Konfiguration unter Linux mag ich mich schon fast nicht mehr äußern. Wie immer verweigere ich “pulseaudio” (das mit Mehrkanalkarten bis heute nicht vernünftig bzw. nicht fehlerfrei umgehen kann) für den normalen Gebrauch des Desktops die Dienstaufnahme. Das Abschalten von Pulseaudio ermöglicht unter Opensuse auf einfache Weise eine entsprechende Option unter YaST.

Während mir aber unter KDE 4.14 nach einem Neustart unter “Systemeinstellungen => Mulitimedia” für meine 3 Soundkarten eine ganze Phalanx von funktionstüchtigen Alsa-fähigen Geräten unter den Phonon-Einstellungen angezeigt wurde/wird, ist dies bei Opensuse Leap 42.1 mit KDE 5 nicht der Fall.

Das ist durchaus ein Problem: Durch diesen “Fehler” (?) fallen dann auch Änderungen an der Priorität der Devices und ein einfacher Tests unter den Tisch. Solche Dinge treiben mich als Anwender zu Verzweiflungsausbrüchen.

phonon1

Interessanterweise zeigt aber Amarok 2.8.0, das noch für KDE 4.14.17 entwickelt wurde, unter dem Menüpunkt “Einstellungen > Amarok einrichten => Wiedergabe => Phonon einrichten” alle Alsa-tauglichen Geräte inkl. der selbstdefinierten Alsa-Devices korrekt an!

amarok1

amarok

Es liegt also nicht an der Hardware, nicht am Alsa-System, nicht am Gstreamer-Backend, sondern an irgendwelchen Lücken zwischen der Darstellung der Phonon-Konfiguration unter KDE 5 und Alsa. Da ich zumindest für meine XONAR D2X spezielle Alsa-Profile für den Multikanalbetrieb eingerichtet habe, benötige ich eigentlich die Anzeige der darüber definierten eigenen Alsa-Geräte unter KDE5. Daher mein dringender Appell:

Liebe KDE- und/oder Opensuse-Entwickler: Bitte orientiert euch bei der Programmierung der Masken für die Phonon-Einstellungen nicht ausschließlich an den Features eines (nach wie vor unzureichenden) Pulseaudio-Interfaces. Es gibt Leute, die wollen aus guten Gründen mit einem direkt nutzbaren Alsa-Interface (ohne fehlerhafte Pulseaudio-Zwischenschichten) leben!

Denn auch bei aktiviertem Pulseaudio wird neben den üblichen Problemen (plötzliches Springen auf 100% Lautstärke bei Systemnachrichten; die erzwungene gekoppelte Regelung der Lautstärke von verschiedenen Input und Output-Kanälen, massive Fehler in der Mehrkanal-Regelung, dauerndes ungwolltes Resetten der individuiell gesetzten Lautstärke pro Ausgangskanal auf einen gemeinsamen Wert….) nur eine gebündelte Darstellung von Audiooptionen angeboten – aber nicht die gegliederte Darstellung der alsafähigen Subdevices.

Ich hatte alles in allem erhebliche Probleme, unter Opensuse Leap 42.1 meine Xonar D2X neben der Createive XiFi in der Form zum Laufen zu bringen, wie ich das von Opensuse 13.2 gewohnt war. Aber eine Beschreibung der Schritte
würde diesen Artikel sprengen.

Auch Amarok funktionierte nach der Grundinstallation zunächst überhaupt nicht – auch nicht mit Ogg Vorbis-Dateien. Man muss sich da durchkämpfen. In jedem Fall sollte man eine Reihe von sound-relatierten Paketen auf die unter den Packman-Repositories und im Opensuse Multimedia Repository angebotenen Versionen umstellen. Vorabtests selbst definierter Alsa-Devices sind zumindest im Moment noch über das Phonon-Interface von Amarok (aus dem Packman Repository) möglich

Sonstiges

systemd ist unter Leap 42.1 wegen des SLES12-Unterbaus noch auf einem veralteten Stand (Version 210). So stehen die für bestimmte Zwecke (wie Virtualisierung) nützlichen Möglichkeiten zur permanenten Konfiguration von z.B. “veth”-Devices in der Datei “/etc/systemd/network” unter Leap 42.1 noch nicht zur Verfügung (wohl aber unter Tumbleweed; systemd-Version 225).

Zur Umgehung eines Problems mit YaST auf Mehrschirm-Systemen siehe
Opensuse Leap 42.1 – nerviger YaST Bug

Erstes Fazit

Insgesamt komme ich nach nun 2 Monaten intensiver Alltags- und Entwicklungs-Arbeit unter Leap sowie vielen Einzeltests zur Sound/Video-Einrichtung wie dem Ausprobieren von KVM/VMware-WS unter Opensuse Leap 42.1 zu dem Schluss, dass z.B. Opensuse 13.2 mit KDE 4.14 immer noch stabiler läuft als Leap 42.1 und sogar mehr Funktionalität bietet.

Inzwischen nutze ich Leap 42.1 aber zunehmend produktiv.

Dennoch habe ich mich zeitweise schon gefragt, ob ich mich nicht langsam nach einer anderen Distribution umsehen sollte. Vielleicht wechsle ich auf meine alten Tage sogar zum Gnome-Desktop. Da ich z.Z. öfter mal mit Debian- und Kali-Systemen arbeite, zeichnet sich die Richtung schon ab ….

Linux bridges – can iptables be used against MiM attacks based on ARP spoofing ? – III

This small series of blog contributions was written to understand a little better how to use iptables in the context of Linux bridges as a countermeasure against some of the effects of a man-in-the-middle [MiM] attack based on ARP spoofing. The attacking system as well as the attacked systems are in our scenarios attached to Linux bridge ports. My objective was to block redirected TCP/IP packets from and to the attacking system.

In the first post
Linux bridges – can iptables be used against MiM attacks based on ARP spoofing ? – I
we had discussed how we have to set up iptables rules for ports of a single Linux bridge and their associated IP-addresses to get the desired blocking. We found that a certain order of DENY and ACCEPT rules is required.

In the second post
Linux bridges – can iptables be used against MiM attacks based on ARP spoofing ? – II
we investigated how iptables reacts to the existence of multiple and linked Linux bridges on one and the same host.

We defined “border ports” as ports that connect a Linux bridge to other bridges, to external network segments or to the virtualization host itself – but not to guests directly attached to the bridge via tap or veth-devices. “Border ports” may be passed by packets traveling to their destination across multiple bridges. We then extended our previous considerations on iptables rules to such “border ports” and found a general recipe for the order of the required DENY and ACCEPT rules for the ports of the multiple bridges.

In the present post we shall test the required rules for the bridge setup presented in the 2nd post. We consider some examples of attack variants with respect to the 2 bridges and ICMP packets. However, the tests would also work for any TCP based service. The reason is that the central DENY rules are very general and compiled without reference to any specific service type.

I assume that you have had a look at the screenshots of the logical rules displayed in a FWbuilder interface in the 2nd article (II).

Setup and iptables rules created by FWbuilder

See the following drawing for the setup of our test scenario:

bridge3

The general DENY rules and the ICMP-related ACCEPT rules displayed in the last article are compiled by FWbuilder to create the following script commands:

    # Rule 2 (vk63)
    # 
    echo "Rule 2 (vk63)"
    # 
    # virbr6 guest port
    $IPTABLES -N Out_RULE_2
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk63 !  -d 192.168.50.13   -j Out_RULE_2  
    $IPTABLES -A Out_RULE_2  -j LOG  --log-level info --log-prefix "RULE 2 -- DENY "
    $IPTABLES -A Out_RULE_2  -j DROP
    # 
    # Rule 3 (vk64)
    # 
    echo "Rule 3 (vk64)"
    # 
    # virbr6 guest port
    $IPTABLES -N Out_RULE_3
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk64 !  -d 192.168.50.14   -j Out_RULE_3 
    $IPTABLES -A Out_RULE_3  -j LOG  --log-level info --log-prefix "RULE 3 -- DENY "  
    $IPTABLES -A Out_RULE_3  -j DROP
    # 
    # Rule 4 (vk65)
    # 
    echo "Rule 4 (vk65)"
    # 
    # virbr6 guest port
    $IPTABLES -N Out_RULE_4
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk65 !  -d 192.168.50.15   -j Out_RULE_4  
    $IPTABLES -A Out_RULE_4  -j LOG  --log-level info --log-prefix "RULE 4 -- DENY "    
    $IPTABLES -A Out_RULE_4  -j DROP
    # 
    # Rule 5 (vk42)
    # 
    echo "Rule 5 (vk42)"
    # 
    # virbr4 guest port
    $IPTABLES -N Out_RULE_5
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk42 !  -d 192.168.50.12   -j Out_RULE_5  
    $IPTABLES -A Out_RULE_5  -j LOG  --log-level info --log-prefix "RULE 5 -- DENY "
    $IPTABLES -A Out_RULE_5  -j DROP
    # 
    # Rule 6 (vk63)
    # 
    echo "Rule 6 (vk63)"
    # 
    # virbr6 guest port
    $IPTABLES -N Out_RULE_6
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk63  -s 192.168.50.13   -j Out_RULE_6  
    $IPTABLES -A Out_RULE_6  -j LOG  --log-level info --log-prefix "RULE 6 -- DENY "
    $IPTABLES -A Out_RULE_6  -j DROP
    # 
    # Rule 7 (vk64)
    # 
    echo "Rule 7 (vk64)"
    # 
    # virbr6 guest port
    $IPTABLES -N Out_RULE_7
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk64  -s 192.168.50.14   -j Out_RULE_7  
    $IPTABLES -A Out_RULE_7  -j LOG  --log-level info --log-prefix "RULE 7 -- DENY "
    $IPTABLES -A Out_RULE_7  -j DROP
    # 
    # Rule 8 (vk65)
    # 
    echo "Rule 8 (vk65)"
    # 
    # virbr6 guest port
    $IPTABLES -N Out_RULE_8
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk65  -s 192.168.50.15   -j Out_RULE_8  
    $IPTABLES -A Out_RULE_8  -j LOG  --log-level info --log-prefix "RULE 8 -- DENY "
    $IPTABLES -A Out_RULE_8  -j DROP
    # 
    # Rule 9 (vk42)
    # 
    echo "Rule 9 (vk42)"
    # 
    # virbr4 guest port
    $IPTABLES -N Out_RULE_9
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vk42  -s 192.168.50.12   -j Out_RULE_9  
    $IPTABLES -A Out_RULE_9  -j LOG  --log-level info --log-prefix "RULE 9 -- DENY "
    $IPTABLES -A Out_RULE_9  -j DROP
    # 
    # Rule 10 (vk63)
    # 
    echo "Rule 10 (vk63)"
    # 
    # virbr6 guest port
    $IPTABLES -N In_RULE_10
    $IPTABLES -A INPUT -m physdev --physdev-in vk63 !  -s 192.168.50.13   -j In_RULE_10
    $IPTABLES -A FORWARD -m physdev --physdev-in vk63 !  -s 192.168.50.13   -j In_RULE_10  
    $IPTABLES -A In_RULE_10  -j LOG  --log-level info --log-prefix "RULE 10 -- DENY "
    $IPTABLES -A In_RULE_10  -j DROP
    # 
    # Rule 11 (vk64)
    # 
    echo "Rule 11 (vk64)"
    # 
    # virbr6 guest port
    $IPTABLES -N In_RULE_11
    $IPTABLES -A INPUT -m physdev --physdev-in vk64 !  -s 192.168.50.14   -j In_RULE_11
    $IPTABLES -A FORWARD -m physdev --physdev-in vk64 !  -s 192.168.50.14   -j In_RULE_11  
    $IPTABLES -A In_RULE_11  -j LOG  --log-level info --log-prefix "RULE 11 -- DENY "
    $IPTABLES -A In_RULE_11  -j DROP
    # 
    # Rule 12 (vk65)
    # 
    echo "Rule 12 (vk65)"
    # 
    # virbr6 guest port
    $IPTABLES -N In_RULE_12
    $IPTABLES -A INPUT -m physdev --physdev-in vk65 !  -s 192.168.50.15   -j In_RULE_12
    $IPTABLES -A FORWARD -m physdev --physdev-in vk65 !  -s 192.168.50.15   -j In_RULE_12  
    $IPTABLES -A In_RULE_12  -j LOG  --log-level info --log-prefix "RULE 12 -- DENY "
    $IPTABLES -A In_RULE_12  -j DROP
    # 
    # Rule 13 (vk42)
    # 
    echo "Rule 13 (vk42)"
    # 
    # virbr4 guest port
    $IPTABLES -N In_RULE_13
    $IPTABLES -A INPUT -m physdev --physdev-in vk42 !  -s 192.168.50.12   -j In_RULE_13
    $IPTABLES -A FORWARD -m physdev --physdev-in vk42 !  -s 192.168.50.12   -j In_RULE_13   
    $IPTABLES -A In_RULE_13  -j LOG  --log-level info --log-prefix "RULE 13 -- DENY "
    $IPTABLES -A In_RULE_13  -j DROP
    # 
    # Rule 15 (vethb2)
    # 
    echo "Rule 15 (vethb2)"
    # 
    # br6 border out
    $IPTABLES -N Cid7404X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vethb2  -j Cid7404X2034.0
    $IPTABLES -A Cid7404X2034.0  -s 192.168.50.13   -j RETURN
    $IPTABLES -A Cid7404X2034.0  -s 192.168.50.14   -j RETURN
    $IPTABLES -A Cid7404X2034.0  -s 192.168.50.15   -j RETURN
    $IPTABLES -N Out_RULE_15_3
    $IPTABLES -A Cid7404X2034.0  -j Out_RULE_15_3
    $IPTABLES -A Out_RULE_15_3  -j LOG  --log-level info --log-prefix "RULE 15 -- DENY "  
    $IPTABLES -A Out_RULE_15_3  -j DROP
    # 
    # Rule 16 (vethb2)
    # 
    echo "Rule 16 (vethb2)"
    # 
    # br6 border out
    $IPTABLES -N Cid7478X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vethb2  -j Cid7478X2034.0  
    $IPTABLES -A Cid7478X2034.0  -d 192.168.50.1   -j RETURN
    $IPTABLES -A Cid7478X2034.0  -d 192.168.0.37   -j RETURN
    $IPTABLES -A Cid7478X2034.0  -d 192.168.50.12   -j RETURN
    $IPTABLES -N Out_RULE_16_3
    $IPTABLES -A Cid7478X2034.0  -j Out_RULE_16_3
    $IPTABLES -A Out_RULE_16_3  -j LOG  --log-level info --log-prefix "RULE 16 -- DENY "
    $IPTABLES -A Out_RULE_16_3  -j DROP
    # 
    # Rule 17 (vethb1)
    # 
    echo "Rule 17 (vethb1)"
    # 
    # br4 border out
    $IPTABLES -N Cid8637X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vethb1  -j Cid8637X2034.0  
    $IPTABLES -A Cid8637X2034.0  -s 192.168.50.1   -j RETURN
    $IPTABLES -A Cid8637X2034.0  -s 192.168.0.37   -j RETURN
    $IPTABLES -A Cid8637X2034.0  -s 192.168.50.12   -j RETURN
    $IPTABLES -N Out_RULE_17_3
    $IPTABLES -A Cid8637X2034.0  -j Out_RULE_17_3
    $IPTABLES -A Out_RULE_17_3  -j LOG  --log-level info --log-prefix "RULE 17 -- DENY "
    $IPTABLES -A Out_RULE_17_3  -j DROP
    # 
    # Rule 18 (vethb1)
    # 
    echo "Rule 18 (vethb1)"
    # 
    # br4 border out
    $IPTABLES -N Cid8753X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vethb1  -j Cid8753X2034.0  
    $IPTABLES -A Cid8753X2034.0  -d 192.168.50.13   -j RETURN
    $IPTABLES -A Cid8753X2034.0  -d 192.168.50.14   -j RETURN
    $IPTABLES -A Cid8753X2034.0  -d 192.168.50.15   -j RETURN
    $IPTABLES -N Out_RULE_18_3
    $IPTABLES -A Cid8753X2034.0  -j Out_RULE_18_3
    $IPTABLES -A Out_RULE_18_3  -j LOG  --log-level info --log-prefix "RULE 18 -- DENY "
    $IPTABLES -A Out_RULE_18_3  -j DROP
    # 
    # Rule 19 (vmh1)
    # 
    echo "Rule 19 (vmh1)"
    # 
    # br4 border out
    $IPTABLES -N Cid8374X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vmh1  -j Cid8374X2034.0  
    $IPTABLES -A Cid8374X2034.0  -s 192.168.50.12   -j RETURN
    $IPTABLES -A Cid8374X2034.0  -s 192.168.50.13   -j RETURN
    $IPTABLES -A Cid8374X2034.0  -s 192.168.50.14   -j RETURN
    $IPTABLES -A Cid8374X2034.0  -s 192.168.50.15   -j RETURN
    $IPTABLES -N Out_RULE_19_3
    $IPTABLES -A Cid8374X2034.0  -j Out_RULE_19_3
    $IPTABLES -A Out_RULE_19_3  -j LOG  --log-level info --log-prefix "RULE 19 -- DENY "
    $IPTABLES -A Out_RULE_19_3  -j DROP
    # 
    # Rule 20 (vmh1)
    # 
    echo "Rule 20 (vmh1)"
    # 
    # br4 border out
    $IPTABLES -N Cid8530X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-is-bridged --physdev-out vmh1  -j Cid8530X2034.0  
    $IPTABLES -A Cid8530X2034.0  -d 192.168.50.1   -j RETURN
    $IPTABLES -A Cid8530X2034.0  -d 192.168.0.37   -j RETURN
    $IPTABLES -N Out_RULE_20_3
    $IPTABLES -A Cid8530X2034.0  -j Out_RULE_20_3
    $IPTABLES -A Out_RULE_20_3  -j LOG  --log-level info --log-prefix "RULE 20 -- DENY "
    $IPTABLES -A Out_RULE_20_3  -j DROP
    # 
    # Rule 22 (vethb2)
    # 
    echo "Rule 22 (vethb2)"
    # 
    # br6 border IN
    $IPTABLES -N Cid7917X2034.0
    $IPTABLES -A INPUT -m physdev --physdev-in vethb2  -j Cid7917X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb2  -j Cid7917X2034.0
    $IPTABLES -A Cid7917X2034.0  -d 192.168.50.13   -j RETURN
    $IPTABLES -A Cid7917X2034.0  -d 192.168.50.14   -j RETURN
    $IPTABLES -A Cid7917X2034.0  -d 192.168.50.15   -j RETURN
    $IPTABLES -N In_RULE_22_3
    $IPTABLES -A Cid7917X2034.0  -j In_RULE_22_3
    $IPTABLES -A In_RULE_22_3  -j LOG  --log-level info --log-prefix "RULE 22 -- DENY "
    $IPTABLES -A In_RULE_22_3  -j DROP
    # 
    # Rule 23 (vethb2)
    # 
    echo "Rule 23 (vethb2)"
    # 
    # br6 border IN
    $IPTABLES -N Cid8013X2034.0
    $IPTABLES -A INPUT -m physdev --physdev-in vethb2  -j Cid8013X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb2  -j Cid8013X2034.0
    $IPTABLES -A Cid8013X2034.0  -s 192.168.50.1   -j RETURN
    $IPTABLES -A Cid8013X2034.0  -s 192.168.0.37   -j RETURN
    $IPTABLES -A Cid8013X2034.0  -s 192.168.50.12   -j RETURN
    $IPTABLES -N In_RULE_23_3
    $IPTABLES -A Cid8013X2034.0  -j In_RULE_23_3
    $IPTABLES -A In_RULE_23_3  -j LOG  --log-level info --log-prefix "RULE 23 -- DENY "
    $IPTABLES -A In_RULE_23_3  -j DROP
    # 
    # Rule 24 (vethb1)
    # 
    echo "Rule 24 (vethb1)"
    # 
    # br4 border IN
    $IPTABLES -N Cid7639X2034.0
    $IPTABLES -A INPUT -m physdev --physdev-in vethb1  -j Cid7639X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb1  -j Cid7639X2034.0
    $IPTABLES -A Cid7639X2034.0  -s 192.168.50.13   -j RETURN
    $IPTABLES -A Cid7639X2034.0  -s 192.168.50.14   -j RETURN
    $IPTABLES -A Cid7639X2034.0  -s 192.168.50.15   -j RETURN
    $IPTABLES -N In_RULE_24_3
    $IPTABLES -A Cid7639X2034.0  -j In_RULE_24_3
    $IPTABLES -A In_RULE_24_3  -j LOG  --log-level info --log-prefix "RULE 24 -- DENY "  
    $IPTABLES -A In_RULE_24_3  -j DROP
    # 
    # Rule 25 (vethb1)
    # 
    echo "Rule 25 (vethb1)"
    # 
    # br4 border IN
    $IPTABLES -N Cid7736X2034.0
    $IPTABLES -A INPUT -m physdev --physdev-in vethb1  -j Cid7736X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb1  -j Cid7736X2034.0
    $IPTABLES -A Cid7736X2034.0  -d 192.168.50.1   -j RETURN
    $IPTABLES -A Cid7736X2034.0  -d 192.168.0.37   -j RETURN
    $IPTABLES -A Cid7736X2034.0  -d 192.168.50.12   -j RETURN
    $IPTABLES -N In_RULE_25_3
    $IPTABLES -A Cid7736X2034.0  -j In_RULE_25_3
    $IPTABLES -A In_RULE_25_3  -j LOG  --log-level info --log-prefix "RULE 25 -- DENY "  
    $IPTABLES -A In_RULE_25_3  -j DROP
    # 
    # Rule 26 (vmh1)
    # 
    echo "Rule 26 (vmh1)"
    # 
    # br4 border IN
    $IPTABLES -N Cid8881X2034.0
    $IPTABLES -A INPUT -m physdev --physdev-in vmh1  -j Cid8881X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vmh1  -j Cid8881X2034.0
    $IPTABLES -A Cid8881X2034.0  -s 192.168.50.1   -j RETURN
    $IPTABLES -A Cid8881X2034.0  -s 192.168.0.37   -j RETURN
    $IPTABLES -N In_RULE_26_3
    $IPTABLES -A Cid8881X2034.0  -j In_RULE_26_3
    $IPTABLES -A In_RULE_26_3  -j LOG  --log-level info --log-prefix "RULE 26 -- DENY "  
    $IPTABLES -A In_RULE_26_3  -j DROP
    # 
    # Rule 27 (vmh1)
    # 
    echo "Rule 27 (vmh1)"
    # 
    # br4 border IN
    $IPTABLES -N Cid9010X2034.0
    $IPTABLES -A INPUT -m physdev --physdev-in vmh1  -j Cid9010X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vmh1  -j Cid9010X2034.0
    $IPTABLES -A Cid9010X2034.0  -d 192.168.50.12   -j RETURN
    $IPTABLES -A Cid9010X2034.0  -d 192.168.50.13   -j RETURN
    $IPTABLES -A Cid9010X2034.0  -d 192.168.50.14   -j RETURN
    $IPTABLES -A Cid9010X2034.0  -d 192.168.50.15   -j RETURN
    $IPTABLES -N In_RULE_27_3
    $IPTABLES -A Cid9010X2034.0  -j In_RULE_27_3
    $IPTABLES -A In_RULE_27_3  -j LOG  --log-level info --log-prefix "RULE 27 -- DENY "  
    $IPTABLES -A In_RULE_27_3  -j DROP
    # 
    # Rule 29 (vmh2)
    # 
    echo "Rule 29 (vmh2)"
    # 
    # host OUT
    $IPTABLES -N Cid10297X2034.0
    $IPTABLES -A OUTPUT -o vmh2   -s 192.168.0.19   -j Cid10297X2034.0
    $IPTABLES -A OUTPUT -o vmh2   -s 192.168.50.1   -j Cid10297X2034.0
    $IPTABLES -A Cid10297X2034.0  -d 192.168.50.12   -j RETURN
    $IPTABLES -A Cid10297X2034.0  -d 192.168.50.13   -j RETURN
    $IPTABLES -A Cid10297X2034.0  -d 192.168.50.14   -j RETURN
    $IPTABLES -A Cid10297X2034.0  -d 192.168.50.15   -j RETURN
    $IPTABLES -N Out_RULE_29_3
    $IPTABLES -A Cid10297X2034.0  -j Out_RULE_29_3
    $IPTABLES -A Out_RULE_29_3  -j LOG  --log-level info --log-prefix "RULE 29 -- DENY "  
    $IPTABLES -A Out_RULE_29_3  -j DROP
    # 
    # Rule 30 (vmh2)
    # 
    echo "Rule 30 (vmh2)"
    # 
    # host IN
    $IPTABLES -N Cid10437X2034.0
    $IPTABLES -A INPUT -i vmh2   -d 192.168.0.19   -j Cid10437X2034.0
    $IPTABLES -A INPUT -i vmh2   -d 192.168.50.1   -j Cid10437X2034.0
    $IPTABLES -A FORWARD -i vmh2   -d 192.168.0.37   -j Cid10437X2034.0
    $IPTABLES -A Cid10437X2034.0  -s 192.168.50.12   -j RETURN
    $IPTABLES -A Cid10437X2034.0  -s 192.168.50.13   -j RETURN
    $IPTABLES -A Cid10437X2034.0  -s 192.168.50.14   -j RETURN
    $IPTABLES -A Cid10437X2034.0  -s 192.168.50.15   -j RETURN
    $IPTABLES -N In_RULE_30_3
    $IPTABLES -A Cid10437X2034.0  -j In_RULE_30_3
    $IPTABLES -A In_RULE_30_3  -j LOG  --log-level info --log-prefix "RULE 30 -- DENY "  
    $IPTABLES -A In_RULE_30_3  -j DROP
    # 
    # Rule 32 (vk63)
    # 
    echo "Rule 32 (vk63)"
    # 
    # br6 IN
    $IPTABLES -N In_RULE_32
    $IPTABLES -A INPUT -m physdev --physdev-in vk63 -p icmp  -m icmp  -s 192.168.50.13   --icmp-type any  -m state --state NEW  -j In_RULE_32  
    $IPTABLES -A FORWARD -m physdev --physdev-in vk63 -p icmp  -m icmp  -s 192.168.50.13   --icmp-type any  -m state --state NEW  -j In_RULE_32  
    $IPTABLES -A In_RULE_32  -j LOG  --log-level info --log-prefix "RULE 32 -- ACCEPT "
    $IPTABLES -A In_RULE_32  -j ACCEPT
    # 
    # Rule 33 (vk64)
    # 
    echo "Rule 33 (vk64)"
    # 
    # br6 IN
    $IPTABLES -N In_RULE_33
    $IPTABLES -A INPUT -m physdev --physdev-in vk64 -p icmp  -m icmp  -s 192.168.50.14   --icmp-type any  -m state --state NEW  -j In_RULE_33  
    $IPTABLES -A FORWARD -m physdev --physdev-in vk64 -p icmp  -m icmp  -s 192.168.50.14   --icmp-type any  -m state --state NEW  -j In_RULE_33  
    $IPTABLES -A In_RULE_33  -j LOG  --log-level info --log-prefix "RULE 33 -- ACCEPT "
    $IPTABLES -A In_RULE_33  -j ACCEPT
    # 
    # Rule 34 (vk64)
    # 
    echo "Rule 34 (vk64)"
    # 
    # br6 IN HTTP
    $IPTABLES -N In_RULE_34
    $IPTABLES -A FORWARD -m physdev --physdev-in vk64 -p tcp -m tcp  -m multiport  -s 192.168.50.14   -d 192.168.0.37   --dports 80,443  -m state --state NEW  -j In_RULE_34   
    $IPTABLES -A In_RULE_34  -j LOG  --log-level info --log-prefix "RULE 34 -- ACCEPT "
    $IPTABLES -A In_RULE_34  -j ACCEPT
    # 
    # Rule 35 (vk65)
    # 
    echo "Rule 35 (vk65)"
    # 
    # br6 IN
    $IPTABLES -N In_RULE_35
    $IPTABLES -A INPUT -m physdev --physdev-in vk65 -p icmp  -m icmp  -s 192.168.50.15   --icmp-type any  -m state --state NEW  -j In_RULE_35
    $IPTABLES -A FORWARD -m physdev --physdev-in vk65 -p icmp  -m icmp  -s 192.168.50.15   --icmp-type any  -m state --state NEW  -j In_RULE_35
    $IPTABLES -A In_RULE_35  -j LOG  --log-level info --log-prefix "RULE 35 -- ACCEPT "
    $IPTABLES -A In_RULE_35  -j ACCEPT
    # 
    # Rule 36 (vethb2)
    # 
    echo "Rule 36 (vethb2)"
    # 
    # br6 border IN
    $IPTABLES -N Cid9698X2034.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb2 -p icmp  -m icmp  -s 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid9698X2034.0  
    $IPTABLES -N In_RULE_36
    $IPTABLES -A Cid9698X2034.0  -d 192.168.50.13   -j In_RULE_36
    $IPTABLES -A Cid9698X2034.0  -d 192.168.50.14   -j In_RULE_36
    $IPTABLES -A Cid9698X2034.0  -d 192.168.50.15   -j In_RULE_36
    $IPTABLES -N Cid9698X2034.1
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb2 -p icmp  -m icmp  -s 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid9698X2034.1  
    $IPTABLES -A Cid9698X2034.1  -d 192.168.50.13   -j In_RULE_36
    $IPTABLES -A Cid9698X2034.1  -d 192.168.50.14   -j In_RULE_36
    $IPTABLES -A Cid9698X2034.1  -d 192.168.50.15   -j In_RULE_36
    $IPTABLES -N Cid9698X2034.2
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb2 -p icmp  -m icmp  --icmp-type any  -m state --state NEW  -j Cid9698X2034.  2
    $IPTABLES -N Cid9698X2034.3
    $IPTABLES -A Cid9698X2034.2  -s 192.168.0.37   -j Cid9698X2034.3
    $IPTABLES -A Cid9698X2034.2  -s 192.168.50.12   -j Cid9698X2034.3
    $IPTABLES -A Cid9698X2034.3  -d 192.168.50.13   -j In_RULE_36
    $IPTABLES -A Cid9698X2034.3  -d 192.168.50.14   -j In_RULE_36
    $IPTABLES -A Cid9698X2034.3  -d 192.168.50.15   -j In_RULE_36
    $IPTABLES -A In_RULE_36  -j LOG  --log-level info --log-prefix "RULE 36 -- ACCEPT "
    $IPTABLES -A In_RULE_36  -j ACCEPT
    # 
    # Rule 38 (vk42)
    # 
    echo "Rule 38 (vk42)"
    # 
    # br4 IN
    $IPTABLES -N In_RULE_38
    $IPTABLES -A INPUT -m physdev --physdev-in vk42 -p icmp  -m icmp  -s 192.168.50.12   --icmp-type any  -m state --state NEW  -j In_RULE_38
    $IPTABLES -A FORWARD -m physdev --physdev-in vk42 -p icmp  -m icmp  -s 192.168.50.12   --icmp-type any  -m state --state NEW  -j In_RULE_38  
    $IPTABLES -A In_RULE_38  -j LOG  --log-level info --log-prefix "RULE 38 -- ACCEPT "
    $IPTABLES -A In_RULE_38  -j ACCEPT
    # 
    # Rule 39 (vethb1)
    # 
    echo "Rule 39 (vethb1)"
    # 
    # br4 border IN
    $IPTABLES -N Cid15566X9203.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb1 -p icmp  -m icmp  -d 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid15566X9203.0  
    $IPTABLES -N In_RULE_39
    $IPTABLES -A Cid15566X9203.0  -s 192.168.50.13   -j In_RULE_39
    $IPTABLES -A Cid15566X9203.0  -s 192.168.50.14   -j In_RULE_39
    $IPTABLES -A Cid15566X9203.0  -s 192.168.50.15   -j In_RULE_39
    $IPTABLES -N Cid15566X9203.1
    $IPTABLES -A INPUT -m physdev --physdev-in vethb1 -p icmp  -m icmp  -d 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid15566X9203.1  
    $IPTABLES -A Cid15566X9203.1  -s 192.168.50.13   -j In_RULE_39
    $IPTABLES -A Cid15566X9203.1  -s 192.168.50.14   -j In_RULE_39
    $IPTABLES -A Cid15566X9203.1  -s 192.168.50.15   -j In_RULE_39
    $IPTABLES -N Cid15566X9203.2
    $IPTABLES -A FORWARD -m physdev --physdev-in vethb1 -p icmp  -m icmp  --icmp-type any  -m state --state NEW  -j Cid15566X9203.2  
    $IPTABLES -N Cid15566X9203.3
    $IPTABLES -A Cid15566X9203.2  -d 192.168.0.37   -j Cid15566X9203.3
    $IPTABLES -A Cid15566X9203.2  -d 192.168.50.12   -j Cid15566X9203.3
    $IPTABLES -A Cid15566X9203.3  -s 192.168.50.13   -j In_RULE_39
    $IPTABLES -A Cid15566X9203.3  -s 192.168.50.14   -j In_RULE_39
    $IPTABLES -A Cid15566X9203.3  -s 192.168.50.15   -j In_RULE_39
    $IPTABLES -A In_RULE_39  -j LOG  --log-level info --log-prefix "RULE 39 -- ACCEPT "
    $IPTABLES -A In_RULE_39  -j ACCEPT
    # 
    # Rule 40 (vmh1)
    # 
    echo "Rule 40 (vmh1)"
    # 
    # br4 border IN
    $IPTABLES -N Cid16232X9203.0
    $IPTABLES -A FORWARD -m physdev --physdev-in vmh1 -p icmp  -m icmp  -s 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid16232X9203.0  
    $IPTABLES -N In_RULE_40
    $IPTABLES -A Cid16232X9203.0  -d 192.168.50.12   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.0  -d 192.168.50.13   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.0  -d 192.168.50.14   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.0  -d 192.168.50.15   -j In_RULE_40
    $IPTABLES -N Cid16232X9203.1
    $IPTABLES -A FORWARD -m physdev --physdev-in vmh1 -p icmp  -m icmp  -s 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid16232X9203.1   
    $IPTABLES -A Cid16232X9203.1  -d 192.168.50.12   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.1  -d 192.168.50.13   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.1  -d 192.168.50.14   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.1  -d 192.168.50.15   -j In_RULE_40
    $IPTABLES -N Cid16232X9203.2
    $IPTABLES -A FORWARD -m physdev --physdev-in vmh1 -p icmp  -m icmp  -s 192.168.0.37   --icmp-type any  -m state --state NEW  -j Cid16232X9203.2  
    $IPTABLES -A Cid16232X9203.2  -d 192.168.50.12   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.2  -d 192.168.50.13   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.2  -d 192.168.50.14   -j In_RULE_40
    $IPTABLES -A Cid16232X9203.2  -d 192.168.50.15   -j In_RULE_40
    $IPTABLES -A In_RULE_40  -j LOG  --log-level info --log-prefix "RULE 40 -- ACCEPT "
    $IPTABLES -A In_RULE_40  -j ACCEPT
    # 
    # Rule 42 (vmh2)
    # 
    echo "Rule 42 (vmh2)"
    # 
    # external are IN
    $IPTABLES -N Cid16691X6788.0
    $IPTABLES -A INPUT -i vmh2  -p icmp  -m icmp  -d 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid16691X6788.0  
    $IPTABLES -N In_RULE_42
    $IPTABLES -A Cid16691X6788.0  -s 192.168.50.12   -j In_RULE_42
    $IPTABLES -A Cid16691X6788.0  -s 192.168.50.13   -j In_RULE_42
    $IPTABLES -A Cid16691X6788.0  -s 192.168.50.14   -j In_RULE_42
    $IPTABLES -A Cid16691X6788.0  -s 192.168.50.15   -j In_RULE_42
    $IPTABLES -N Cid16691X6788.1
    $IPTABLES -A FORWARD -i vmh2  -p icmp  -m icmp  -d 192.168.0.37   --icmp-type any  -m state --state NEW  -j Cid16691X6788.1  
    $IPTABLES -A Cid16691X6788.1  -s 192.168.50.12   -j In_RULE_42
    $IPTABLES -A Cid16691X6788.1  -s 192.168.50.13   -j In_RULE_42
    $IPTABLES -A Cid16691X6788.1  -s 192.168.50.14   -j In_RULE_42
    $IPTABLES -A Cid16691X6788.1  -s 192.168.50.15   -j In_RULE_42
    $IPTABLES -A In_RULE_42  -j LOG  --log-level info --log-prefix "RULE 42 -- ACCEPT "
    $IPTABLES -A In_RULE_42  -j ACCEPT
    # 
    # Rule 43 (vmh2)
    # 
    echo "Rule 43 (vmh2)"
    # 
    # host border OUT
    $IPTABLES -N Cid16236X6788.0
    $IPTABLES -A OUTPUT -o vmh2  -p icmp  -m icmp  -s 192.168.50.1   --icmp-type any  -m state --state NEW  -j Cid16236X6788.0  
    $IPTABLES -N Out_RULE_43
    $IPTABLES -A Cid16236X6788.0  -d 192.168.50.12   -j Out_RULE_43
    $IPTABLES -A Cid16236X6788.0  -d 192.168.50.13   -j Out_RULE_43
    $IPTABLES -A Cid16236X6788.0  -d 192.168.50.14   -j Out_RULE_43
    $IPTABLES -A Cid16236X6788.0  -d 192.168.50.15   -j Out_RULE_43
    $IPTABLES -N Cid16236X6788.1
    $IPTABLES -A FORWARD -o vmh2  -p icmp  -m icmp  -s 192.168.0.37   --icmp-type any  -m state --state NEW  -j Cid16236X6788.1  
    $IPTABLES -A Cid16236X6788.1  -d 192.168.50.12   -j Out_RULE_43
    $IPTABLES -A Cid16236X6788.1  -d 192.168.50.13   -j Out_RULE_43
    $IPTABLES -A Cid16236X6788.1  -d 192.168.50.14   -j Out_RULE_43
    $IPTABLES -A Cid16236X6788.1  -d 192.168.50.15   -j Out_RULE_43
    $IPTABLES -A Out_RULE_43  -j LOG  --log-level info --log-prefix "RULE 43 -- ACCEPT "
    $IPTABLES -A Out_RULE_43  -j ACCEPT
    # 
    # Rule 45 (br0)
    # 
    echo "Rule 45 (br0)"
    # 
    # external
    $IPTABLES -A OUTPUT -o br0   -m state --state NEW  -j ACCEPT
    # 
    # Rule 46 (br0)
    # 
    echo "Rule 46 (br0)"
    # 
    # external
    $IPTABLES -A FORWARD -i br0   -s 192.168.0.10   -d 192.168.0.255   -m state --state NEW  -j ACCEPT
    $IPTABLES -A INPUT -i br0   -s 192.168.0.10   -d 192.168.0.255   -m state --state NEW  -j ACCEPT
    $IPTABLES -A INPUT -i br0   -s 192.168.0.10   -m state --state NEW  -j ACCEPT
    # 
    # Rule 47 (global)
    # 
    echo "Rule 47 (global)"
    # 
    $IPTABLES -N RULE_47
    $IPTABLES -A OUTPUT  -j RULE_47
    $IPTABLES -A INPUT  -j RULE_47
    $IPTABLES -A FORWARD  -j RULE_47
    $IPTABLES -A RULE_47  -j LOG  --log-level info --log-prefix "RULE 47 -- DENY "
    $IPTABLES -A RULE_47  -j DROP

The variable “$IPTABLES” identifies the local iptables command. As already discussed in the last articles we arranged our (virtual) guest systems, the virtualization host and external systems in 3 defined host groups in FWbuilder (see the last post):

  • br6_grp: kali3, kali4, kali5,
  • br4_grp: kali2,
  • ext_grp: the host and some external web server “lamp”.

Remember that rules for bridge-ports are investigated separately and independently as a packet moves from one bridge to another. Note that the host and further systems attached to “virbr4” via a veth device “vmh2” are recognized as members of a distinct logical host area for which iptables rules again are reinvestigated separatly by the kernel during packet transport. Therefore we need ACCEPT rules to allow for incoming and outgoing packets at the host’s interface “vmh2”.

Examples of spoofing scenarios

With 2 bridges in place we can define already a variety of ARP spoofing scenarios with a subsequent MiM-attack. We only test some selected, but typical scenarios. Note again that we cannot prevent the act of spoofing itself with iptables – however, we can prevent that redirected packets arrive at the MiM system.

Example 1: kali2 of virbr4 attacks the communication between kali3 and kali5 within virbr6

Which rule do we expect to prevent this? Actually as kali2 tries to redirect the intended communication from bridge virbr6 into bridge virbr4 we would already expect a DENY rule at the border port “vethb2” to stop redirected packets. In our rules list this would be rule 16.

So let us see. We start ARP spoofing on kali2:

root@kali2: ~# echo 1 > /proc/sys/net/ipv4/ip_forward
root@kali2: ~# iptables -A OUTPUT -p icmp --icmp-type redirect -j REJECT
root@kali2: ~# arpspoof -i eth3 -t 192.168.50.13 192.168.50.14 & 2> /dev/null  
root@kali2: ~# arpspoof -i eth3 -t 192.168.50.14 192.168.50.13 & 2> /dev/null  

eth3 is the relevant Ethernet interface to net 192.168.50.0/24 on guest kali2.
After some time we get the following ARP information on e.g. kali3:

bridges_1

Consequently, after a “journalctl -f” on the virtualization host we find: :

Mar 17 13:21:53 mytux kernel: RULE 16 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vethb2 MAC=52:54:00:f2:a4:8d:52:54:00:b1:5d:1f:08:00 SRC=192.168.50.13 DST=192.168.50.14 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=16140 DF PROTO=ICMP TYPE=8 CODE=0 ID=1756 SEQ=1   
Mar 17 13:21:54 mytux kernel: RULE 16 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vethb2 MAC=52:54:00:f2:a4:8d:52:54:00:b1:5d:1f:08:00 SRC=192.168.50.13 DST=192.168.50.14 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=16252 DF PROTO=ICMP TYPE=8 CODE=0 ID=1756 SEQ=2  
 

Our test example shows that rules for border ports help to isolate bridges against misguided packets.

Rule 16 deserves a closer look as it contains a logical negation of 2 separately defined groups of hosts. We see that FWbuilder compiles the negation internally correctly: The related subchain definition contains all required hosts.

As described in the previous articles we stop the attack by the command “killall arpspoof” on kali2. Remember that due to time limits for ARP and port caching information on the guests and the bridge, respectively, it may take some time until normal operation is possible again. See the first article of this series for more information on this topic.

Example 2: kali2 of virbr4 attacks the communication between kali3 and the virtualization host

In this scenario a regular (!) packet would propagate from virbr6 through virbr4 and then to the host. Therefore, neither border port rules for virbr6 nor for virbr4 can block the traffic. We must, instead, rely on the analysis of redirected packets following an OUT direction to port vk42 – this is rule 5.

Therefore, this example is just a repetition of what we learned in the first article of this series
Linux bridges – can iptables be used against MiM attacks based on ARP spoofing ? – I

Actually, after another spoofing attack by kali2

root@kali2: ~# arpspoof -i eth3 -t 192.168.50.1 192.168.50.13 & 2> /dev/null  
root@kali2: ~# arpspoof -i eth3 -t 192.168.50.13 192.168.50.1 & 2> /dev/null  

and sending pings from kali3 to the host we get:

Mar 17 18:44:51 mytux kernel: RULE 32 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vethb2 MAC=54:00:f2:a4:8d:52:54:00:b1:5d:1f:08:00 SRC=192.168.50.13 DST=192.168.50.1 LEN=84 TOS=0x00 PREC=0x00 =64 ID=48428 DF PROTO=ICMP TYPE=8 CODE=0 ID=2872 SEQ=2   
Mar 17 18:44:51 mytux kernel: RULE 5 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vk42 MAC=52:50:f2:a4:8d:52:54:00:b1:5d:1f:08:00 SRC=192.168.50.13 DST=192.168.50.1 LEN=84 TOS=0x00 PREC=0x00 TTL ID=48428 DF PROTO=ICMP TYPE=8 CODE=0 ID=2872 SEQ=2 
   

We see that the transition from bridge virbr6 to virbr4 works as planned – however the packets redirected to the MiM kali2 are stopped at vk42. Good!

Example 3: kali3 of virbr6 attacks the communication between kali4 of virbr4 and the virtualization host

We look at pings issued from the host to kali4 after an attack of kali3. In this case the border port rules again must not block. Instead, we rely on local port rules at port vk63, .i.e. rule 2. Indeed:

Mar 17 19:00:39 mytux kernel: RULE 43 -- ACCEPT IN= OUT=vmh2 SRC=192.168.50.1 DST=192.168.50.14 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53576 DF PROTO=ICMP TYPE=8 CODE=0 ID=2981 SEQ=1 
Mar 17 19:00:39 mytux kernel: RULE 40 -- ACCEPT IN=virbr4 OUT=virbr4 PHYSIN=vmh1 PHYSOUT=vethb1 MAC=52:54:00:b1:5d:1f:7a:ff:fc:bd:68:b6:08:00 SRC=192.168.50.1 DST=192.168.50.14 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53576 DF PROTO=ICMP TYPE=8 CODE=0 ID=2981 SEQ=1       
Mar 17 19:00:39 mytux kernel: RULE 2 -- DENY IN=virbr6 OUT=virbr6 PHYSIN=vethb2 PHYSOUT=vk63 MAC=52:54:00:b1:5d:1f:7a:ff:fc:bd:68:b6:08:00 SRC=192.168.50.1 DST=192.168.50.14 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=53576 DF PROTO=ICMP TYPE=8 CODE=0 ID=2981 SEQ=1     
  

Example 4: kali3 of virbr6 attacks the communication between kali2 and the virtualization host

In this case border rules should stop redirected packets. For our test case this would in particular be rule 18.

And – after the initialization of the attack by kali3 and the trial to send pings from kali2 to the host, we get:

Mar 18 16:47:30 mytux kernel: RULE 18 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vk42 PHYSOUT=vethb1 MAC=52:54:00:b1:5d:1f:52:54:00:f2:a4:8d:08:00 SRC=192.168.50.12 DST=192.168.50.1 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=4405 DF PROTO=ICMP TYPE=8 CODE=0 ID=2420 SEQ=1    
  

And vice versa :

Mar 18 16:48:42 mytux kernel: RULE 43 -- ACCEPT IN= OUT=vmh2 SRC=192.168.50.1 DST=192.168.50.12 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=63251 DF PROTO=ICMP TYPE=8 CODE=0 ID=21778 SEQ=1  
Mar 18 16:48:42 mytux kernel: RULE 18 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vmh1 PHYSOUT=vethb1 MAC=52:54:00:b1:5d:1f:f2:be:a1:5a:cd:6e:08:00 SRC=192.168.50.1 DST=192.168.50.12 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=63251 DF PROTO=ICMP TYPE=8 CODE=0 ID=21778 SEQ=1   
   

As expected!

Example 5: The host attacks communication between guests attached to an inner bridge

One may think such an example is just academic. Actually, in my opinion it is not. Although the administrator of a virtualization host has in principle a variety of means available to follow any communication across a bridge, ARP spoofing should NOT be such a measure under normal operation conditions. In addition, there may be legal aspects in a professional hosting situation.

But more important: From the perspective of the involved bridges, in our setup the host is attached to bridge virbr4 as an external guest over a border port. Rules for the virtualization host are, therefore, only an example for similar rules applied to other external hosts which may have the allowance to communicate with bridge guests – via forwarding and a respective route defined on the virtualization host.

We expect rule 20 to stop packages redirected by the MiM:

Mar 18 17:38:38 rux kernel: RULE 32 -- ACCEPT IN=virbr6 OUT=virbr6 PHYSIN=vk63 PHYSOUT=vethb2 MAC=f2:be:a1:5a:cd:6e:52:54:00:b1:5d:1f:08:00 SRC=192.168.50.13 DST=192.168.50.12 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=36173 DF PROTO=ICMP TYPE=8 CODE=0 ID=2218 SEQ=1   
Mar 18 17:38:38 rux kernel: RULE 20 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vethb1 PHYSOUT=vmh1 MAC=f2:be:a1:5a:cd:6e:52:54:00:b1:5d:1f:08:00 SRC=192.168.50.13 DST=192.168.50.12 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=36173 DF PROTO=ICMP TYPE=8 CODE=0 ID=2218 SEQ=1   
 

And vice versa

Mar 18 17:39:39 rux kernel: RULE 20 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vk42 PHYSOUT=vmh1 MAC=f2:be:a1:5a:cd:6e:52:54:00:f2:a4:8d:08:00 SRC=192.168.50.12 DST=192.168.50.13 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=10910 DF PROTO=ICMP TYPE=8 CODE=0 ID=2730 SEQ=1    
Mar 18 17:39:40 rux kernel: RULE 20 -- DENY IN=virbr4 OUT=virbr4 PHYSIN=vk42 PHYSOUT=vmh1 MAC=f2:be:a1:5a:cd:6e:52:54:00:f2:a4:8d:08:00 SRC=192.168.50.12 DST=192.168.50.13 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=11113 DF PROTO=ICMP TYPE=8 CODE=0 ID=2730 SEQ=2    
 

Summary

So, all in all, for our few examples we could verify that our recipe for setting up iptables rules in case of several linked Linux bridges with guests on one [KVM] virtualization host guided us correctly. After associating unique IP addresses with bridge ports we can define rules that block the transport of packets redirected to a MiM system – even when multiple bridges are present on the virtualization host. Additional and special rules for the bridges’ border ports help to prevent irregular traffic between defined groups of guests and/or external hosts.

Note that we only demonstrated this for specific allowance rules for the ICMP protocol. Yet, it is easy to understand that the very same principles should work for any protocol on level 4.