Introducing iRules LX

iRules is a powerful scripting  language that allows you to  control network traffic in real time that can route, redirect, modify, drop, log or do just about anything else with network traffic passing through a BIG-IP proxy. iRules enables network programmability to consolidate functions across applications and services.

iRules LX: The Next Evolution of Network Programmability

iRules LX is the next stage of evolution for network programmability that brings Node.js language support to the BIG-IP with version 12.1.  Node.js allows JavaScript developers access to over 250,000 npm packages that makes code easier to write and maintain.  Development teams can access and work on code with the new iRules LX Workspace environment and the new plug-in available for the Eclipse IDE and can be used for continuous integration builds.

Extend with Node.js Support

iRules Language eXtensions (LX) enables node.js capabilities on the BIG-IP platform.  By using iRules LX (Node.js) extensions you can program iRules LX using javascript to control network traffic in real time.

There are only three simple Tcl commands that you need to extend iRules to Node.js

  • ILX::init – create Tcl handle, bind extension
  • ILX::call – send/receive data to/from extension method
  • ILX::notify – send data one-way to method, no response

There is only one Node.js command to know:

  • ILX.addMethod – create method, for use by above

Share and Reuse Code

iRules LX makes it easy for JavaScript developers to share and reuse Node.js code, and it makes it easy to update the code.  These bits of reusable code are called packages, or sometimes modules.  This makes it possible for you to compose complex business logic in node.js without sophisticated developers who know Tcl in-depth.  There are over 250,000 npm packages that makes code easier to write and maintain.

iRules LX Workspaces

LX Workspaces provides a portable development package environment with NPM-like directory structure that acts as the source for creation and modification of plugins and node.js extensions.  Workspace LX is developer-friendly, with Git-like staging and publishing that simplifies export and import to keep your files in sync for continuous integration and development.

iRules IDE Integration / Eclipse Plugin

Available soon, F5 is delivering a free downloadable plugin to enable our customers to develop iRules & iRules LX scripts via their own Eclipse IDE. The plugin provides modern IDE editing capabilities including syntax highlighting & validation, code completion, and code formatting for both Tcl and Javascript. This plugin will enable authenticated push and pull of iRules code files to and from the BIG-IP platform. This simplifies development workflows by enabling our customers to develop iRules scripts in a familiar development environment.

iRules LX Example: Use Node.js to Make an Off Box MySQL Lookup

This iRules example shows a connection to MySQL database via Node.js. This example uses Tcl iRulesand JavaScript code to make a MySQL call.  The iRules prompts the user for a basic auth username and password, then we lookup the username in a MySQL database table and return the user's groups.  

MySQL iRule (Tcl) Example:

###################################  
# mysql_irulelx  
# 2015-05-17 - Aaron Hooley - First draft example showing iRulesLX querying MySQL database  
###################################  
when RULE_INIT {  
    # Enable logging to /var/log/ltm?  
    # 0=none, 1=error, 2=verbose  
    set static::mysql_debug 2  
}  
when HTTP_REQUEST {  
  
    if {$static::mysql_debug >= 2}{log local0. "New HTTP request. \[HTTP::username\]=[HTTP::username]"}  
    # If the client does not supply an HTTP basic auth username, prompt for one.  
    # Else send the HTTP username to node.js and respond to client with result  
    if {[HTTP::username] eq ""}{  
  
        HTTP::respond 401 content "Username was not included in your request.\nSend an HTTP basic auth username to test" WWW-Authenticate {Basic realm="iRulesLX example server"}  
        if {$static::mysql_debug >= 2}{log local0. "No basic auth username was supplied. Sending 401 to prompt client for username"}  
  
    } else {  
  
        # Do some basic validation of the HTTP basic auth username  
        # First fully URI decode it using https://devcentral.f5.com/s/articles?sid=523  
        set tmp_username [HTTP::username]  
        set username [URI::decode $tmp_username]  
  
        # Repeat decoding until the decoded version equals the previous value  
        while { $username ne $tmp_username } {  
            set tmp_username $username  
            set username [URI::decode $tmp_username]  
        }  
        # Check that username only contains valid characters using https://devcentral.f5.com/s/articles?sid=726  
        if {$username ne [set invalid_chars [scan $username {%[-a-zA-Z0-9_]}]]}{  
            HTTP::respond 401 content "\nA valid username was not included in your request.\nSend an HTTP basic auth username to test\n" WWW-Authenticate {Basic realm="iRulesLX example server"}  
            if {$static::mysql_debug >= 1}{log local0. "Invalid characters in $username. First invalid character was [string range $username [string length $invalid_chars] [string length $invalid_chars]]"}  
  
            # Exit this iRule event  
            return  
        }  
        # Client supplied a valid username, so initialize the iRulesLX extension  
        set RPC_HANDLE [ILX::init mysql_extension]  
        if {$static::mysql_debug >= 2}{log local0. "\$RPC_HANDLE: $RPC_HANDLE"}  
       
        # Make the call and save the iRulesLX response  
        # Pass the username and debug level as parameters  
        #set rpc_response [ILX::call $RPC_HANDLE myql_nodejs $username $static::mysql_debug]  
        set rpc_response [ILX::call $RPC_HANDLE myql_nodejs $username]  
        if {$static::mysql_debug >= 2}{log local0. "\$rpc_response: $rpc_response"}  
  
        # The iRulesLX rule will return -1 if the query succeeded but no matching username was found  
        if {$rpc_response == -1}{  
            HTTP::respond 401 content "\nYour username was not found in MySQL.\nSend an HTTP basic auth username to test\n" WWW-Authenticate {Basic realm="iRulesLX example server"}  
            if {$static::mysql_debug >= 1}{log local0. "Username was not found in MySQL"}  
        } elseif {$rpc_response eq ""}{  
            HTTP::respond 401 content "\nDatabase connection failed.\nPlease try again\n" WWW-Authenticate {Basic realm="iRulesLX example server"}  
            if {$static::mysql_debug >= 1}{log local0. "MySQL query failed"}  
        } else {  
            # Send an HTTP 200 response with the groups retrieved from the iRulesLX plugin  
            HTTP::respond 200 content "\nGroup(s) for '$username' are '$rpc_response'\n"  
            if {$static::mysql_debug >= 1}{log local0. "Looked up \$username=$username and matched group(s): $rpc_response"}  
        }  
    }  
}  
 

 

 

MySQL iRule LX (Node.js) Example

 

/* ###################################### */  
/* index.js counterpart for mysql_irulelx */  
  
/* Log debug to /var/log/ltm? 0=none, 1=errors only, 2=verbose */  
var debug = 2;  
if (debug >= 2) {console.log('Running extension1 index.js');}  
  
/* Import the f5-nodejs module. */  
var f5 = require('f5-nodejs');  
  
/* Create a new rpc server for listening to TCL iRule calls. */  
var ilx = new f5.ILXServer();  
  
/* Start listening for ILX::call and ILX::notify events. */  
ilx.listen();  
  
/* Add a method and expect a username parameter and reply with response */  
ilx.addMethod('myql_nodejs', function(username, response) {  
  
    if (debug >= 1) {console.log('my_nodejs' + ' ' + typeof(username.params()) + ' = ' + username.params());}  
  
    var mysql = require('mysql');  
    var connection = mysql.createConnection({  
        host     : '10.0.0.110',  
        user     : 'bigip',  
        password : 'bigip'  
    });  
  
    // Connect to the MySQL server  
    connection.connect(function(err) {  
        if (err) {  
            if (debug >= 1) {console.error('Error connecting to MySQL: ' + err.stack);}  
            return;  
        }  
        if (debug >= 2) {console.log('Connected to MySQL as ID ' + connection.threadId);}  
    });  
  
    // Perform the query. Escape the user-input using mysql.escape: https://www.npmjs.com/package/mysql#escaping-query-values  
    connection.query('SELECT * from users_db.users_table where name = ' + mysql.escape(username.params(0)), function(err, rows, fields) {  
        if (err) {  
            // MySQL query failed for some reason, so send a null response back to the Tcl iRule  
            if (debug >= 1) {console.error('Error with query: ' + err.stack);}  
            response.reply('');  
            return;  
        } else {  
            // Check for no result from MySQL  
            if (rows < 1){  
                if (debug >= 1) {console.log('No matching records from MySQL');}  
  
                // Return -1 to the Tcl iRule to show no matching records from MySQL  
                response.reply('-1');  
            } else {  
                if (debug >= 2) {console.log('First row from MySQL is: ', rows[0]);}  
                //Return the group field from the first row to the Tcl iRule  
                response.reply(rows.pop());  
            }  
        }  
    });  
    // Close the MySQL connection  
    connection.end();  
});  

Getting Started

To get started with iRules LX, download a BIG-IP trial and follow the upcoming tutorials available on the DevCentral community.

Published May 13, 2016
Version 1.0

Was this article helpful?