cancel
Showing results for 
Search instead for 
Did you mean: 
Login & Join the DevCentral Connects Group to watch the Recorded LiveStream (May 12) on Basic iControl Security - show notes included.

Problem this snippet solves:

This iRule implements a simple SMTP proxy. This is just to show an idea to relay smtp message and to route mail traffic based on FROM or TO... this rule is tested and work with one default pool and basic smtp client.

Code :

when CLIENT_ACCEPTED {
    set chelo ""
    set cfrom ""
    set crcpt ""    
    TCP::respond "220\r\n"
    log local0. "client accepted"
    TCP::collect
}

when CLIENT_DATA {

    set cdata [TCP::payload]
    if { [ string length $cdata ] <= 0 } {
        return
    }
    if { not ( $cdata contains "\r\n" ) } {
        log local0. "get <$cdata> so far"
        return
    }
    if { $cdata starts_with "HELO" } {
        set chelo [TCP::payload]
        log local0. "get helo <$cdata>"
        TCP::respond "250 OK\r\n"
        TCP::payload replace 0 [string length $chelo] ""
        return
    }
    if { $cdata starts_with "MAIL FROM:" } {
        set cfrom [TCP::payload]
        log local0. "get from <$cfrom>"
        TCP::respond "250 OK\r\n"
        TCP::payload replace 0 [string length $cfrom] ""
        return
    }
    if { $cdata starts_with "RCPT TO:" } {
        set crcpt "$crcpt[TCP::payload]"
        log local0. "get rcpt <$crcpt>"
        TCP::respond "250 OK\r\n"
        TCP::payload replace 0 [string length [TCP::payload]] ""
        return
    }
    if { $cdata starts_with "DATA" } {
        log local0. "get data <$cdata>"
        TCP::payload replace 0 0 $chelo$cfrom$crcpt
    }
    log local0. "payload [TCP::payload]"
    TCP::release
    TCP::collect
}

when SERVER_CONNECTED {
    log "server connected"
    TCP::collect
}

when SERVER_DATA {
    set sdata [TCP::payload]

    if { $sdata starts_with "220" } {
        log local0. "get data <$sdata>"
        TCP::payload replace 0 [string length $sdata] ""
        return
    }
    if { $sdata contains "\r\n354 " } {
        log local0. "get data <$sdata>"
        TCP::payload replace 0 [string length $sdata] "354\r\n"
    }
    if { [ string length $sdata ] <= 0 } {
        return
    }
    log local0. "payload <[TCP::payload]>"
    TCP::release
    TCP::collect
}
when CLIENT_CLOSED {
    log local0. "client closed"
}
Comments
F5_Jeff
Cirrus
Cirrus

Hi,

 

We are also constructing iRule which will direct traffic on a specific pool if an email was received from "gmail.com" or "google.com"

 

here is our script:

 

when CLIENT_ACCEPTED { log local0. "Client_ACCEPTED_HIT: IP address:[IP::client_addr]" TCP::respond "220\r\n" TCP::collect } when CLIENT_DATA { log local0. "Client_DATA_HIT: IP address:[IP::client_addr] Data: [TCP::payload]" if { [TCP::payload] contains "gmail.com" } { pool testtesttest log local0. "cdata_FOUND: IP address:[IP::client_addr] Dest IP:[server_addr]:[server_port] Data: [TCP::payload]" } TCP::release TCP::collect }

The issue we are having is that, the traffic is not being directed to the specified pool but to the default pool. however, the log "cdata_FOUND:.." can be seen. This mean that the traffic satisfied the "if condition".

 

This is where we are lost.

 

Dave_Hergert
Nimbostratus
Nimbostratus

HI F5_Jeff -

So SMTP is a tricky protocol when it comes to intercepting it within the F5 iRules.

 

In the example `mailx` output below, each exchange is a separate set of rules firing in the LTM irule. So the "connecting..." line will fire your CLIENT_ACCEPTED event. Then each >>> will fire your CLIENT_DATA event, which needs to respond with a response code and status. Basically being a middleman between your client and the smtp server. So if you want to do the load balancing up front, you need to intercept the commands up until the RCPT TO command, make the determination, and THEN route.

 

There is an example here, https://devcentral.f5.com/s/question/0D51T00006j2svT/irule-based-on-smtp-receiver-domain where its "queuing" or saving up the HELO and FROM commands in variables and responding to the client, and then once a RCPT comes through and the destination domain is known, it "replays" the saved commands to the back end SMTP server (after picking a pool).

 

I will admit, I have not been able to get it working perfectly (yet) but hopefully this helps explain the way SMTP communications are handled in the F5 LTM iRules, where its a series of events that you need to orchestrate (and its not trivial).

echo "Testing" | mailx -v -r "testing-sender@example.com" -s "New test email through LTM" -S smtp="smtp-vip.example.com:25" testing-recipient@example.com   Resolving host smtp-vip.example.com . . . done. Connecting to 10.1.2.3:25 . . . connected. 220 EXGHOST03.example.com Microsoft ESMTP MAIL Service ready at Thu, 16 Jan 2020 08:59:52 -0500 >>> HELO mydesktop.example.com 250 EXGHOST03.example.com Hello [10.4.5.6] >>> MAIL FROM:<testing-sender@example.com> 250 2.1.0 Sender OK >>> RCPT TO:<testing-recipient@example.com> 250 2.1.5 Recipient OK >>> DATA 354 Start mail input; end with <CRLF>.<CRLF> >>> . 250 2.6.0 <5c216d54./ukd0CI0g7ZM5ydM%testing-sender@example.com> [InternalId=106902174639121, Hostname=EXGHOST03.example.com] Queued mail for delivery >>> QUIT 221 2.0.0 Service closing transmission channel

 

Version history
Last update:
‎18-Mar-2015 15:36
Updated by:
Contributors