Rule- option/targetoptin Plugins (IPTables interface only)

As the iptables command utility is really fast developing and every few week new options are added, all ruleoption handling is outsourced to small easy to write plugins.

The underlieing document format is designed flexible enougth to allow the implementaiton of any command option iptables knows (or will know, if i didn't forget anything).

The rule creation engine can be extend by registering new option/targetoption types defined in small XML files that are parsed at startup. And the editor GUI can embeed the edit widgets needed to configure the ruleoption.

Parts of an Ruleoption/targetoption Plugin

  • A XML file named p.e. kmfruleoption_mac_option.xml

  • A desktop file p.e. kmfruleoptionedit_mac.desktop that registeres the plugin within the KDE services framework.

  • An KParts Plugin component/class that derives form either KMFRuleOptionEditInterface or KMFRuleTargetOptionEditInterface named p.e. KMFRuleOptionEditMAC

  • A Class deriving from QWidget that may be loaded into the editor view when editing that option.

    Most likely you'll use QTDesigner to design the GUI. If you do this you need to inherit from the generated class.

XML File describing the rule option

The xml file gets loaded during application startup and registers the specified rule option in the engine. Have a look at some of the other XML files installed in $KDE-PREFIX/share/apps/kmyfirewall/ruleoptions for some more example of how to define the needed options.

<!DOCTYPE kmyfirewall-kmfruleoptiondefinition>
<ruleoptiondefinitionset>
<ruleoptiondefinition name="mac_opt"  guiName="MAC Address">
  <option guiName="" command="--match mac" />
  <option guiName="MAC:" command="--mac-source" />
</ruleoptiondefinition>
</ruleoptiondefinitionset>

The .desktop file

This file is installed into the services directory and makes the plugin available for the KDE plugin framework

The important thing here is that you set Type=Service and the right SeviceType. The application only searches for plugins with SeviceType=KMyFirewall/RuleOptionEdit or SeviceType=KMyFirewall/RuleTargetOptionEdit.

The line X-KDE-Library=libkmfruleoptionedit_mac lets the KDE plugin framework know which library defines the functionality.

[Desktop Entry]
Encoding=UTF-8
Type=Service
Name=KMyFirewall MAC option edit
Name[da]=KMyFirewall MAC redigeringstilvalg
Comment=Plugin for edition MAC address based iptables rule options
Comment[da]=Plugin ril at redigere MAC adressebaserede regeltilvalg for iptables
X-KDE-Library=libkmfruleoptionedit_mac
ServiceTypes=KMyFirewall/RuleOptionEdit

The Makefile.am

Make shure that you add the option -module $(KDE_PLUGIN) to the <library_name>_LDFLAGS. Otherwise the plugin can not be loaded at runtime.

INCLUDES = -I../kmfwidgets -I../../ipteditor -I$(srcdir)/../interfaces $(all_includes)
METASOURCES = AUTO
kde_module_LTLIBRARIES =  libkmfruleoptionedit_mac.la

libkmfruleoptionedit_mac_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries)
libkmfruleoptionedit_mac_la_LIBADD = ../../core/libkmfcore.la ../../interfaces/libkmfinterfaces.la $(LIB_KPARTS)
noinst_HEADERS =  kmfruleoptioneditmac.h kmfruleeditmac.h

libkmfruleoptionedit_mac_la_SOURCES = kmfruleoptioneditmac.cpp kmfruleeditmac.cpp  kmyfirewallruleeditormac.ui

partdesktopdir = $(kde_servicesdir)
partdesktop_DATA = kmfruleoptionedit_mac.desktop

install-data-local:
	$(mkinstalldirs) $(DESTDIR)$(kde_datadir)/kmyfirewall/ruleoptions/
	$(INSTALL_DATA)  $(srcdir)/kmfruleoption_mac_option.xml $(DESTDIR)$(kde_datadir)/kmyfirewall/ruleoptions/kmfruleoption_mac_option.xml

uninstall-local:
	-rm -f $(DESTDIR)$(kde_datadir)/kmyfirewall/ruleoptions/kmfruleoption_mac_option.xml

KParts Plugin component/class

This class defines the plugin that will be loaded at application startup.

kmfruleoptioneditmac.h

/***************************************************************************
    Copyright (C) 2023 Christian Hubinger
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
// Author: Christian Hubinger <chubinger@gmail.com>, (C) 2023
#ifndef KMFRULEOPTIONEDITMAC_H
#define KMFRULEOPTIONEDITMAC_H

#include "../../interfaces/kmfruleoptioneditinterface.h"

// KDE includes
#include <kparts/part.h>
#include <kparts/plugin.h>
#include <kparts/factory.h>
#include <kxmlgui.h>

#include <qstring.h>
#include <qptrlist.h>

class IPTRule;
class KMFRuleEditMac;
/**
@author Christian Hubinger
*/
class KMFRuleOptionEditMAC : public KMFRuleOptionEditInterface {
	Q_OBJECT
public:
	KMFRuleOptionEditMAC(QObject *parent = 0, const char *name = 0);

	~KMFRuleOptionEditMAC();
	void loadRule( IPTRule* rule );	
	QWidget* editWidget();
	const QString& optionEditName() const;
	const QString& description() const;

private slots:
	void slotAddRuleOption(QString*, QPtrList< QString >* );
	void slotAddTargetOption(QString*, QPtrList< QString >* );
	void slotShowOverview();

private:
	KMFRuleEditMac *m_edit;
	IPTRule *m_rule;
	
};

class KInstance;

class KMFRuleOptionEditMACFactory : public KLibFactory {
	Q_OBJECT
public:
	KMFRuleOptionEditMACFactory( QObject *parent = 0, const char *name = 0 );
	virtual ~KMFRuleOptionEditMACFactory() {
	};
	virtual QObject* createObject( QObject* parent = 0, const char* pname = 0,
	                               const char* name = "QObject",
	                               const QStringList &args = QStringList() );

};

kmfruleoptioneditmac.cpp

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#include "kmfruleoptioneditmac.h"

// QT includes
#include <qstring.h>

// KDE includes
#include <kdebug.h>
#include <klocale.h>

// Project includes
#include "../../core/iptrule.h"
#include "../../interfaces/kmfruleeditinterface.h"
#include "kmfruleeditmac.h"

// Constructor get out plugin into a valid state
KMFRuleOptionEditMAC::KMFRuleOptionEditMAC(QObject *parent, const char *name)
		: KMFRuleOptionEditInterface(parent, name) {
	
	// Initialise the editor class
	m_edit = new KMFRuleEditMac( 0 , "Edit", 0 );
	// hide it for now
	m_edit->hide();

	// Connect the Signals for the editor class to our slots passing them to the app
	connect( m_edit,SIGNAL(sigAddRuleOpt(QString*, QPtrList< QString >* ) ), 
		this,SLOT( slotAddRuleOption(QString*, QPtrList< QString >* ) ) );
	//
	//connect( m_edit,SIGNAL(sigAddTargetOpt(QString*, QPtrList< QString >* ) ), 
	//	this,SLOT( slotAddTargetOption(QString*, QPtrList< QString >* ) ) );
	connect( m_edit,SIGNAL(sigHideMe() ), 
		this,SLOT( slotShowOverview() ) );
}

KMFRuleOptionEditMAC::~KMFRuleOptionEditMAC() {}

// Slot to pass the Signals to the app 
void KMFRuleOptionEditMAC::slotAddRuleOption(QString* name, QPtrList< QString >* values ) {
	if ( KMFRuleEditInterface* ruleedit = dynamic_cast<KMFRuleEditInterface*> ( parent() ) ) {
		ruleedit->addRuleOption( name, values );
	} else {
		kdDebug() << "KMFRuleOptionEditMAC::slotAddRuleOption(): parent() not of type KMFRuleEditInterface" << endl;
	}
}
void KMFRuleOptionEditMAC::slotAddTargetOption(QString* name, QPtrList< QString >* values ) {
	if ( KMFRuleEditInterface* ruleedit = dynamic_cast<KMFRuleEditInterface*> ( parent() ) ) {
		ruleedit->addRuleTargetOption( name, values );
	} else {
		kdDebug() << "KMFRuleOptionEditMAC::slotAddTargetOption(): parent() not of type KMFRuleEditInterface" << endl;
	}
}

// Make the editor hide the plugin widget
void KMFRuleOptionEditMAC::slotShowOverview() {
	if ( KMFRuleEditInterface* ruleedit = dynamic_cast<KMFRuleEditInterface*> ( parent() ) ) {
		ruleedit->showOverview();
	} else {
		kdDebug() << "KMFRuleOptionEditMAC::slotShowOverview(): parent() not of type KMFRuleEditInterface" << endl;
	}
}


// Get info for GUI about the plugin
const QString& KMFRuleOptionEditMAC::optionEditName() const {
	return *( new QString( i18n("MAC Option") ) );
}
const QString& KMFRuleOptionEditMAC::description() const {
	return *( new QString( i18n("This plugin manages the MAC address based options of iptables.") ) );
}

// We must implement this as it is called from the underliening rule editor
void KMFRuleOptionEditMAC::loadRule( IPTRule* rule ) {
	if ( !rule ) {
		kdDebug() << "KMFRuleOptionEditMAC::loadRule( IPTRule* rule ) - rule == 0" <<endl;
		return;
	}
	m_edit->loadRule( rule );
	m_rule = rule;
}

// return a pointer to our editor widget
QWidget* KMFRuleOptionEditMAC::editWidget() {
	if ( ! m_edit ) {
		kdDebug() << "KMFRuleOptionEditMAC::editWidget() - m_edit == 0" << endl;
		return 0;
	}
	return m_edit;
}

// It's usually safe to leave the factory code alone.. with the
// notable exception of the KAboutData data
#include <kaboutdata.h>
#include <klocale.h>

// KInstance* KMFRuleOptionEditMACFactory::s_instance = 0L;
// KAboutData* KMFRuleOptionEditMACFactory::s_about = 0L;

KMFRuleOptionEditMACFactory::KMFRuleOptionEditMACFactory( QObject* parent, const char* name )
		: KLibFactory( parent, name ) {
}

QObject* KMFRuleOptionEditMACFactory::createObject( QObject* parent, const char* name,
        const char*, const QStringList & ) {
	QObject * obj = new KMFRuleOptionEditMAC( parent, name );
	emit objectCreated( obj );
	return obj;
}

extern "C" {
	void* init_libkmfruleoptionedit_mac() {
		return new KMFRuleOptionEditMACFactory;
	}
}
#include "kmfruleoptioneditmac.moc"

The editor widget class

This class defines a widget that can configure the specific rule option.

kmfruleeditmac.h

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/*
Author: Christian Hubinger <chubinger@gmail.com>, (C) 2001-2023
*/


#ifndef KMFRULEEDITMAC_H
#define KMFRULEEDITMAC_H

#include "kmyfirewallruleeditormac.h"

#include <qvariant.h>
#include <qdialog.h>
#include <qptrlist.h>

class IPTRule;
class KMFErrorHandler;
class KMFCheckInput;
class KMFError;

class KMFRuleEditMac : public KMyFirewallRuleEditorMac {
	Q_OBJECT

public:
	KMFRuleEditMac( QWidget* parent = 0, const char* name = 0, WFlags fl = 0 );
	~KMFRuleEditMac();

	void loadRule( IPTRule* );

public slots:
	void accept();
	void slotHelp();
	void reject();

protected:
	bool event( QEvent* );

private:
	KMFCheckInput *m_check_input;
	KMFErrorHandler *m_err_handler;
	KMFError *m_err;
	IPTRule* m_rule;

signals:
	void sigAddRuleOpt( QString*, QPtrList<QString>* );
	void sigHideMe();
};

#endif // KMFRULEEDITMAC_H

kmfruleeditmac.cpp

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/*
Author: Christian Hubinger <chubinger@gmail.com>, (C) 2001-2023
*/

#include "kmfruleeditmac.h"

#include <qcheckbox.h>
#include <qframe.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include <qvariant.h>
#include <qtooltip.h>
#include <qwhatsthis.h>

#include <kdebug.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kapplication.h>

// project includes
#include "../../core/iptrule.h"
#include "../../core/iptchain.h"
#include "../../core/iptable.h"
#include "../../core/kmfdoc.h"
#include "../../core/kmfiptdoc.h"
#include "../../core/kmfcheckinput.h"
#include "../../core/kmferror.h"
#include "../../core/kmferrorhandler.h"

/*
 *  Constructs a KMFRuleEditMac which is a child of 'parent', with the
 *  name 'name' and widget flags set to 'f' 
 *
 *  The dialog will by default be modeless, unless you set 'modal' to
 *  TRUE to construct a modal dialog.
 */
KMFRuleEditMac::KMFRuleEditMac( QWidget* parent, const char* name, WFlags fl )
		: KMyFirewallRuleEditorMac( parent, name, fl ) {
	m_err_handler = new KMFErrorHandler( "KMFRuleEditMac" );
	m_check_input = new KMFCheckInput();
	m_err = new KMFError();


}

/*
 *  Destroys the object and frees any allocated resources
 */
KMFRuleEditMac::~KMFRuleEditMac() {
	// no need to delete child widgets, Qt does it all for us
}

/*
 *  Main event handler. Reimplemented to handle application
 *  font changes
 */
bool KMFRuleEditMac::event( QEvent* ev ) {
	bool ret = QWidget::event( ev );
	if ( ev->type() == QEvent::ApplicationFontChange ) {}
	return ret;
}

void KMFRuleEditMac::loadRule( IPTRule * rule ) {
	kdDebug() << "void KMFRuleEditMac::loadRule( IPTRule * rule )" << endl,
	c_src_mac->setChecked( false );
	c_inv_src_mac->setChecked( false );
	t_src_mac1 ->clear();
	t_src_mac2 ->clear();
	t_src_mac3 ->clear();
	t_src_mac4 ->clear();
	t_src_mac5 ->clear();
	t_src_mac6 ->clear();
	m_rule = rule;
	QString line = "";

	IPTRuleOption* opt = 0;
	opt = m_rule->getOptionForName("mac_opt");
	if ( opt ) {
		QStringList args = opt->getValues();
		QString src, dest;
		line = *args.at(1);
		if ( line.isEmpty() || line == "UNDEFINED"  )
			return;
		if ( line.startsWith( "! " ) ) {
			line = line.right( line.length() - 2 );
			c_inv_src_mac->setChecked( true );
		}
		int num = 1;
		QString part = "";
		c_src_mac->setChecked( true );
		while ( !line.isEmpty() ) {
			int pos = -1;
			pos = line.find( ":" );
			if ( pos < 0 ) {
				part = line;
				line = "";
			} else {
				part = line.left( pos );
				line = line.right( line.length() - ( pos + 1 ) );
			}
			switch ( num ) {
				case 1:
					t_src_mac1 -> setText( part );
					break;
				case 2:
					t_src_mac2 -> setText( part );
					break;
				case 3:
					t_src_mac3 -> setText( part );
					break;
				case 4:
					t_src_mac4 -> setText( part );
					break;
				case 5:
					t_src_mac5 -> setText( part );
					break;
				case 6:
					t_src_mac6 -> setText( part );
					break;
			}
			num++;
		}
	}
}


void KMFRuleEditMac::accept() {
	kdDebug() << "KMFRuleEditMac::accept()" << endl;
	m_rule->chain()->table()->kmfDoc()->startTransaction();
	m_rule->saveState();
	QString tok1 = t_src_mac1->text().upper();
	QString tok2 = t_src_mac2->text().upper();
	QString tok3 = t_src_mac3->text().upper();
	QString tok4 = t_src_mac4->text().upper();
	QString tok5 = t_src_mac5->text().upper();
	QString tok6 = t_src_mac6->text().upper();

	if ( c_src_mac->isChecked() && ( tok1.isEmpty() || tok2.isEmpty() || tok3.isEmpty() || tok4.isEmpty() || tok5.isEmpty() || tok6.isEmpty() ) ) {
		const QString & msg = i18n( "One ore more of the fields are empty. Please fill out all fields to define a valid MAC address." );
		KMessageBox::error( this, msg );
		m_rule->chain()->table()->kmfDoc()->endTransaction();
		return ;
	}

	QString mac = "";

	if ( c_src_mac->isChecked() ) {
		mac = tok1 + ":" + tok2 + ":" + tok3 + ":" + tok4 + ":" + tok5 + ":" + tok6;
		m_check_input->checkInput( mac, "MAC", m_err );
		if ( ! m_err_handler->showError( m_err ) ) {
			m_rule->chain()->table()->kmfDoc()->endTransaction();
			return ;
		}
	}

	QPtrList<QString>* values = new QPtrList<QString>;
	QString* op = new QString( "mac_opt" );
	if ( c_src_mac->isChecked() && !mac.isEmpty() ) {
		kdDebug() << "Add new mac option" << endl;
		QString* src_mac = new QString( mac );
		if ( c_inv_src_mac->isChecked() ) {
			src_mac->prepend( "! " );
		}
		values->append( new QString( "bool:on" ) );
		values->append( src_mac );
	}
	emit sigAddRuleOpt( op, values );
	m_rule->chain()->table()->kmfDoc()->endTransaction();
	emit sigHideMe();
}

void KMFRuleEditMac::slotHelp() {
	kdDebug() << "void KMFRuleEditMac::slotHelp()" << endl;
	kapp->invokeHelp( "src_mac" );
}
void KMFRuleEditMac::reject() {
	kdDebug() << "void KMFRuleEditMac::reject()" << endl;
	emit sigHideMe();
}
#include "kmfruleeditmac.moc"