Venkat Pothamsetty (Cisco), Matthew Franz

[News] [Download] [Installation instructions] [Mailing List]

A port of Modbus Firewall to 2.6.16 has been posted to the project page.Download Page.

As of 5/11/04 A new precompiled version of modbus firewall is available on a CD-ROM based version of Linux so you don't have to build it yourself.

Transparent Modbus/TCP Filtering with Linux


One of the most important trends in industrial networking is the transition from proprietary to standards-based protocols and the accompanying adoption of commercial IT products. Windows-based PC's (often utilizing browser-based code such as Java and ActiveX) are replacing of proprietary HMIs in SCADA, PLC, and distributed control system networks and Ethernet, TCP/IP, wireless protocols are increasingly behind used for data acquisition, device configuration and programming. Modbus/TCP is just one example of how an Industrial protocol has been encapsulated within IP/UDP/TCP with only slight modification—and with little to no concern for security. Since firewall and packet filters are often used to enforce policy within the network where policies could not be easily enforced on end devices or applications, we added support for Modbus/TCP to Linux Netfilter to determine the feasibility of adding fine-grained access controls for a automation protocol within a general purpose firewall devices.

Overview of Modbus/TCP

MODBUS is one of the most widely used messaging standards for communication among industrial devices, including SCADA systems. The messaging standard is a simple request and reply mechanism, very similar to HTTP or SNMP, where a “master” (the client) initiates requests to one or more “slaves” (servers). Masters may be PCs, workstations, mainframes, or even high-end PLCs, while slaves are typically less intelligent PLCs or RTUs that may be directly connected to sensors and actuators. MODBUS typically uses RS-232/485 as its serial transition. Serial MODBUS has two primary implementations: MODBUS ASCII and MODBUS RTU. Modbus/TCP is a third MODBUS protocol which brings MODBUS to IP networks, Modbus/TCP is commonly used by Java applets or ActiveX controls, whereby a user can connected to the embedded web server on an industrial device download the web-based interface for monitoring the end-device and its IO points. It may also be used by an HMI, OPC servers, or software used to program or configure PLCs and network-enable RTUs. Modbus/TCP (also known as the Modbus Application Protocol) uses the reserved (TCP) port of 502 and inc 6 byte header begins the payload that includes the following fields:
Transaction ID (2 bytes)
Protocol ID (2 bytes)
Length (2 bytes)
Unit ID (1 byte)
Immediately following these fields is the function code. The function code is a one byte header (the values can range from 1-255) and tells the receiver of the Modbus PDU what action it is asked to perform. The function codes are usually reads and writes into registers and coils. The read requests are usually performed for diagnostic purposes and the write requests change values in various registers and coils, resulting in sending commands to a end device like a motor. For more detail, see the MODBUS and Modbus/TCP protocol specification. Various function codes perform various operations on a PLC and/or an end device, ranging from diagnostics to actually writing onto various registers of the devices. Therefore any device having access to a given Modbus implementation will be able to perform full range of operations that the implementation supports.

The Need of Application-Layer Filtering

Like most automation protocols (regardless of their transport), Modbus/TCP has no built-in security mechanisms. The protocol has no means of authenticating or authorizing the initiator of the request. Assuming the end-device is network accessible, malicious commands can be sent to it for a variety of objectives. To make matters worse, many of the end devices have no ability to perform packet filtering to even restrict which hosts may connect to the Modbus/TCP slave, let alone specific Modbus/TCP message types. Currently, the only reasonable solution is to filter via a firewall or router access control lists based on TCP port 502. This only permits on denies Modbus/TCP traffic from a given source to a given destination—and provides no control over the type of messages will be processed, and therefore the type of operations that the end device will perform. Depending on the security policy of the organization and the type of application, all hosts may be allowed to perform read operations from a given Modbus/TCP slave (permit from 0x01 through 0x04) , smaller number of hosts maybe allowed to perform write operations, and only a select few devices can program the PLCs (deny function code 0x7E for Modicon PLCs) . Our Netfilter extension makes this sort of fine-grained policy enforcement possible.

Modbus/TCP Support for Linux Netfilter

Design & Implementation

Although packet filtering can be performed on a variety of Open Source platforms, we chose Linux because it is one of the most popular and widely-deployed platforms (especially in the embedded applications) and because Linux Netfilter provides a rich and well documented API for performing a variety of network filtering and packet mangling operations. A large number of extensions for Netfilter are available and made implementation relatively easy. However, Netfilter has a very few filtering hooks at application layer, especially for applications that use TCP as their transport. In fact there is general opposition in the Linux development community towards inclusion of application-layer functionality within the kernel . The conventional wisdom is that application layer operations are best performed by user space code. However, for performance reasons we chose to build this firewall within the kernel. Within the standard Linux kernel, FTP and TFTP are the only examples of application layer protocols and both use connection tracking module called ip_conntrack. They do not support filtering based on application layer header values. For the sake of simplicity and because Modbus/TCP is a stateless protocol, we decided to avoid this route. We decided to model our implementation after the ESP module, which is one of the few modules which does not do filtering based on IP or TCP header values. The ESP module operates at IP layer. It registers its hooks at IP layer and the user space passes on interesting packets to the ESP module operating in the kernel space. It then makes decisions based on the ESP header values. The Modbus module follows the same path, but examines the packets past the TCP header, because Modbus is over TCP.

Ideally, we want the TCP module of Netfilter take care of the TCP handshakes and connection termination and allow our Modbus module to take decisions on Modbus/TCP packets. However, there appears that there is no simple way of telling the TCP module to just deal with handshakes and connection termination. The “state” module knows just the first packet (SYN packet, a “NEW” state) and it assumes the rest of the connection as “ESTABLISHED”, whenever it sees the SYN_ACK reply back.

To overcome this issue, we added additional argument to our module. The “allowtcp” argument takes either a zero or a one. If the argument is zero, our module does not deal with the handshakes and terminations, which is ideal for a default ACCEPT policy. When the user is paranoid and has a default DENY policy, and wants just specific function codes to go through, he does not have to deal with the handshakes and terminations. The “--allowtcp 1” argument matches the packet, and therefore allows the packet in a default DENY policy with a ACCEPT on a specific Modbus/TCP packet.

The modbus module, like any other new Netfilter module requires functionality to be added both within user space and the kernel space. Although netfilter modules make the ultimate decisions to permit, deny, drop, or modify packets, the iptables tool is needed to define (typically based on Layer 3 and 4 headers) which specific rules should be enforced by the kernel. Within user space, libipt_modbus.c was added so that modbus header values could be used as command-line arguments. Within the kernel, ipt_modbus.c provided the match and defined the structure of the Modbus/TCP encapsulation header:

struct modbus_tcp
      struct modbus_hdr
      __u16 transaction_id;
      __u16 protocol_id;
      __u16 length;

      struct modbus_data
      __u8 unit_id;
      __u8 func_code;
      __u16 ref_num;
      __u16 word_cnt;
      __u8 byte_cnt;

Whenever a match occurs, the packet is matched against the defined structure above and the corresponding header values are extracted. The extracted values are then matched against user specified values and appropriate decisions are made.

We decided to support filtering for four header values: function code, unit ID, reference number and the length. The filtering functionality based on the function code is the most important one, we support filtering based on a single function code or a range of function codes. For length, we support filtering based on a single value or any length less than a given value or any length greater than a given value. For the unitID and reference number, we support filtering based on a single specified value.

The user can specify any number of the above supported argument types. If the user spefices just one, the filtering functionality will work based on just that header type and given value for that type. If the user specifes more than one header type and value to filter, the filtering process will pursue a “OR” path: The control will be given to the target chain if a packet header matches with any of the corresponding specified values. We also support inverse function in the Netfilter, where the user can specify to jump to a target if the received packet header value does NOT match a given value.

Example Configuration

Machine A is the machine, which is running the Modbus/TCP server software, typically a HMI, used to configure a Modbus/TCP client. Machine A’s is connected to network, bearing an IP address The Modbus/TCP client is connected to network, bearing an IP address The Modbus/TCP client and the server are connected through a Linux machine F, the Firewall. The machine F is a router between the two networks and therefore has two interfaces. The interface (eth0) on the 10.0 network (connected to the server) has the IP address of and the interface (eth1) on the 20.0 network (connected to the Modbus/TCP device) has the IP address of Now, our goal is to allow just function code 1 (Read Coil) and DROP all other packets.

[Machine A, running any 
Modbus/TCP server 
                                                  [Modbus/TCP client]          
 +++++++++               +++++++++                   
 +       +               +       +                   ++++++++++++++
 +       +---------------+       +------------------ +            +
 +       +        [eth0] +       +[eth1]             ++++++++++++++
 +++++++++               +++++++++

                       [Firewall Machine, F]

Step 1: Configure machine F as a router and make sure that the communication between the server and client works.

ifconfig eth0
(configure eth0 interface)
ifconfig eth1
(configure eth1 interface)
echo "1" /proc/sys/net/ipv4/ip_forward
(enable forwarding from eth0 to eth1 and vice versa)

Step2: Setup a default DROP policy on the FORWARD chain on machine F so that all packets going through are dropped

iptables -P FORWARD DROP 
(default policy on FORWARD chain is DROP)

Step 3: Allow whatever is needed

iptables -A FORWARD -p icmp -j ACCEPT 
(allow pings to go through)
iptables -A FORWARD -p tcp --sport 80 -j ACCEPT
iptables -A FORWARD -p tcp --dport 80 -j ACCEPT 
(allow HTTP traffic if the client is configured through web)

Step4: Allow the desired function code, also allow TCP handshakes and terminations to go through

iptables -A FORWARD -p tcp -m modbus --funccode 1 --allowtcp 1 -j ACCEPT

Testing and Feedback

We have only conducted minimal testing. We did not test the firewall in a real industrial environment. We want the community to test it under their environments and provide us feedback and suggestions.

Feasibility of the Idea

While we know that there is a requirement of a filter based on header values for various industrial protocols, we do not know the practical and real time implications of such a filter. We do not know whether it would be a good idea to have a predetermined filter in mission critial applications. We do not know whether the perfomance impact if any of such a filter.


We followed our experince in enterprise networks in designing the usage. The TARGET chain DROP (which just drops the packet) or DENY (sends a RST packet) may not work at all in industrial environments. The filter may ideally need to send a application error packet to both sides. Based on our experince with these protocols, we implemented filtering out based on a given range of function codes and a “less than/greater than” feature for lenghts, we are not sure that is the best possible way.

New features that need to be in the firewall

Our packet structure is rigid and supports only one packet format. It does not support a packet format where Modbus can have two function codes with in the same packet. We did not frequently encounter such a packet. We also need feedback on benefit of adding functionality to filter based on any other header values other than the supported ones now.


1. Bothamly and Rogerson. "Emerging Ethernet Protocols."

2. Franz, Matthew and Darrin Miller, "Building Secure Industrial Ethernets." Industrial Ethernet Handbook 15.

3. Polsonetti, Chantal. "Industrial Ethernet Protocols: the Next Battleground." Industrial Ethernet Handbook 12.

4. "Modbus Application Protocol Specification version 1.1." December 2002.

5."Modbus Messaging on TCP/IP: Implementation Guide." May 2002

6.Moving industrial protocols to Ethernet TCP/IP

7. Ethernet takes control of the factory floor

SourceForge Logo