BROCADE ADX OPENSCRIPT March 2013 Derek Kang (dkang@brocade.com), Solutions Architect 1 Agenda • OpenScript Architecture • Quick Start • Perl Basics • OpenScript Examples • Best Practices 2 OpenScript Architecture 3 What is OpenScript? Open-Standard (Perl) Based Application Delivery Programming Engine Predictable Scripting Engine with Performance Estimator Learn, Browse, Share Scripts through OpenScript Community www.brocade.com/openscript 4 OpenScript Architecture App Core MP Compile OpenScript Engine (event, context) (result) Content Switching Engine • • Packet Out Packet In TCP/UDP/IP Performance estimation Catching syntax errors • Event Driven Model • Script Attached to Virtual Port • Resource Profile Per Script Limits the resource consumption of a script • 1 MB memory foot print • Watch-dog timer (200 ms) • 100 KB data collection per event 5 OpenScript Quick Start 6 OpenScript Quick Start ADX SLB Config server l7-dont-reset-on-vip-port-fail server real rs1 10.1.1.10 port 80 group-id 10 10 Sorry. Get back later. http://20.1.1.1 server real rs2 10.1.1.11 port 80 group-id 10 10 server virtual v1 20.1.1.1 port 80 script “sorry.pl” bind 80 rs1 80 rs2 80 rs1 rs2 OpenScript “sorry.pl” 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 use OS_SLB; use OS_HTTP_REQUEST; my $page; sub BEGIN { $page = "HTTP/1.1 200 OK\r\nContentLength:21\r\nConnection:close\r\nContentType: text/html; charset=UTF-8\r\n\r\n. Sorry .Get. back later.”; } sub HTTP_REQUEST { OS_SLB::forward (10); } sub SERVER_SELECTION_FAILURE { OS_SLB::reply($page); } 7 OpenScript Quick Start Steps With CLI 1. Configure basic L4 SLB server l7-dont-reset-on-vip-port-fail server real rs1 10.1.1.10 port 80 group-id 10 10 server real rs2 10.1.1.11 port 80 group-id 10 10 server virtual v1 20.1.1.1 port 80 bind 80 rs1 80 rs2 80 2. Write a script on your choice of editor use OS_SLB; use OS_HTTP_REQUEST; sub BEGIN { $page = "HTTP/1.1 200 OK\r\nContentLength:21\r\nConnection:close\r\nContentType: text/html; charset=UTF-8\r\n\r\n. Sorry .Get. back later.”; } sub HTTP_REQUEST { OS_SLB::forward (10); } sub SERVER_SELECTION_FAILURE { OS_SLB::reply($page); } 5. Attach the script to the VIP port 3. Upload the script to the ADX ADX#copy tftp usb0 10.120.61.68 sorry.pl sys\dpscript\sorry.pl ADX#show script all index name In Use 1 sorry.pl 4. Compile the script ADX#copy tftp usb0 10.120.61.68 sorry.pl sys\dpscript\sorry.pl ADX#show script all index name In Use 1 sorry.pl ADX#config term ADX(config)#script compile sorry.pl This script is compiled successfully. Performance for this script: - Approximately 9548 connections/second at 10% CPU utilization - Approximately 47744 connections/second at 50% CPU utilization - Approximately 95488 connections/second at 100% CPU utilization server virtual v1 20.1.1.1 port http script “sorry.pl” 8 OpenScript Quick Start Steps With Web GUI 4 3 1 0 GUI Version Check 2 9 Perl Basics 10 Regex $string $string =~ m/pattern/ =~ s/pattern1/pattern2 my $string = “ab-abc-abcd”; $string =~ m/(.*)-/; print “$1\n”; ab-abc my $string = “ab-abc-abcd”; $string =~ m/(.*?)-/ print “$1\n”; ab my $string = “ab-abc-abcd”; $string =~ s/ab/x/ print “$string”; x-abc-abcd my $string = “ab-abc-abcd”; $string =~ s/ab/x/g print “$string”; x-xc-xcd 11 Hash / List / Array %my_hash_table = ( 1030=>”rs1”, 1031=>”rs2” ) $value = $my_hash_table { 1030 } rs1 @my_array = (“url1”, “url2”, “url3”); $value = $my_array [ 0 ]; url1 @my_two_dim_array = ( [ “url1”, 10 ], [ “url2”, 20 ], [ “url3”, 30 ] ) $value = $my_two_dim_array [ 1 ] [ 1 ] $size = scalar ( @my_array ) 20 3 12 Pack, Unpack, Split, Join and Substr my @ip = split('\.', "10.1.2.3"); my @net_mask = split('\.', "255.255.0.0"); pack (template, list) unpack (template, scalar) my $net_ip = pack("C4", @ip) & pack("C4", @net_mask); my $net = join( '.', unpack("C4", $net_ip) ); 10.1.0.0 print "$net\n"; split (‘separator’, scalar) join (‘joiner’, list) substr (data, start, size) $hex_string = "\x7e\xf1"; my $bin_string = unpack("b*", $hex_string); print "$bin_string\n"; 0111111010001111 my $field = substr ($bin_string, 4, 4); print "$field\n"; 1110 13 Conditional Statements if (EXPR) BLOCK if (EXPR) BLOCK else BLOCK if ( $string == “birthday” ) { print "Happy Birthday!\n"; } elsif ($string == “christmas”) { print "Happy Christmas!\n"; if (EXPR) BLOCK elsif (EXPR) BLOCK if (EXPR) BLOCK elsif (EXPR) BLOCK else BLOCK } else { print "Happy Whatever!\n"; } 14 Loop 2 3 for ($i=2; $i<=3; $i++) { print “$i"; } @url = (url1, url2, url3); foreach $i (@url) { print “$i\n”; } url1 url2 url3 @url = (url1, url2, url3); foreach (@url) { print “$_\n”; } $count = 1; while ($count <= 11 ) { $count++; last; $count++; } print “$count\n”; 2 for $i (2..3) { print “$i"; } 15 OpenScript Examples 16 Mitigate DNS Dynamic Update Attack • What is DNS dynamic update? • To update a DNS record on the DNS server dynamically, e.g., IP address change. • What is DNS dynamic DoS attack? • The attack sends a DNS server a crafted DNS dynamic update message to cause a server crash. • Some DNS Server S/Ws based on BIND 9 software are vulnerable to the attack. • Solution − Drop DNS dynamic update messages using OpenScript 17 Drop DNS Dynamic Update (DNS Dynamic Update Message) (ADX CLI Config) server real rs1 10.1.1.2 port 53 group-id 10 10 ! server real rs2 10.1.1.3 port 53 group-id 10 10 server virtual v1 10.1.1.100 port 53 port 53 script dnsdos.pl bind 53 rs1 53 rs2 (dnsdos.pl) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 use OS_UDP; use OS_SLB; sub UDP_CLIENT_DATA{ $payload = OS_UDP::payload; $mydata = unpack( "b*", $payload); $mystring = substr ($mydata, 17, 4); if ($mystring == "0101" ) { OS_SLB::drop; } else { OS_SLB::forward(10); } } 18 Blocking Access to Certain URLs based on Client IP addresses • You want to allow only Intranet clients to access an URL www.mycompany.com/data/, on your company web server. • Your Intranet clients belong to IP subnet 10.x.x.x/8 • If someone outside tries to access, return a page with a message “You are not allowed to access the content”. All PCs www.mycompany.com www.mycompany.com/datapublic Intranet PCs www.mycompany.com/data www.mycompany.com/data/ www.mycompany.com/data/* 19 Blocking Access to Certain URLs based on Client IP addresses 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 use OS_IP; use OS_SLB; use OS_HTTP_REQUEST; my $local_subnet; my $page; sub BEGIN { $local_subnet = “10.”; $page = "HTTP/1.1 200 OK\r\nContent-Length: 43\r\nConnection: close\r\nContent-Type: text/html; charset=UTF-8\r\n\r\nYou are not allowed to access the content."; } sub HTTP_REQUEST { my $client_ip = OS_IP::src; my $url = OS_HTTP_REQUEST::url; List OpenScript API modules to use Initialization sub routine • Define 10.x.x.x as an internal network • Prepare a web page to return for warning to external clients Main routine • If an external client tries to access www.mycompany.com/data, return a warning page. if (!($client_ip =~ m/^$local_subnet/) && $url =~ m/www.mycompany.com\/data($|\/.*)/) { OS_SLB::reply($page); } else { OS_SLB::forward(1); Regular Expression to match the URL } } 20 SIP VIA Header IP Replacement SIP/2.0 200 OK Via: SIP/2.0/UDP server.foo.com;branch=z9hG4bKnashds8;received=192.0.2.3 Via: SIP/2.0/UDP bigbox.site3.com;branch=z9hG4bK77ef4c2312983.1;received=192.0.2.2 Via: SIP/2.0/UDP pc3.atlanta.com;branch=z9hG4bK776asdhds ;received=192.0.2.1 To: Bob <sip:bob.com>;tag=a6c85cf From: Alice <sip:alice.com>;tag=1928301774 Call-ID: a84b4c76e66710.atlanta.com CSeq: 314159 INVITE Contact: <sip:bob.0.2.4> Content-Type: application/sdp Content-Length: 131 SIP/2.0 200 OK Via: SIP/2.0/UDP server.foo.com;branch=z9hG4bKnashds8;received=10.1.1.100 Via: SIP/2.0/UDP bigbox.site3.com;branch=z9hG4bK77ef4c2312983.1;received=10.1.1.100 Via: SIP/2.0/UDP pc3.atlanta.com;branch=z9hG4bK776asdhds ;received=10.1.1.100 To: Bob <sip:bob.com>;tag=a6c85cf From: Alice <sip:alice.com>;tag=1928301774 Call-ID: a84b4c76e66710.atlanta.com CSeq: 314159 INVITE Contact: <sip:bob.0.2.4> Content-Type: application/sdp Content-Length: 131 21 SIP VIA Header IP Replacement 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 use OS_IP; use OS_UDP; my $vip; sub BEGIN { $vip = 10.1.1.100; # VIP you want to put in the Via header } sub UDP_CLIENT_DATA{ my $client_ip = OS_IP::src; my $sip_msg = OS_UDP::payload; if ($sip_msg =~ s/ (.*?Via:) (.*?;) (.*?;) (received=) (\d{1,3}\. \d{1,3}\. \d{1,3}\. \d{1,3}) / $1 $2 $3 $4 $vip /xig) { OS_UDP::payload($sip_msg); } else { OS_SLB::log (“Warning : SIP client $client_ip : VIA header was not found”); } } 22 OpenScript Best Practices 23 OpenScript Best Practices • Readability • Performance • Trouble Shooting 24 Variable Names and Spaces Around No CamelCase DO my $variable_that_contains_my_server_name = gather_server_stats (gigantic_apache_web_server); DO NOT my $variableThatContainsMyServerName = gatherServerStats (giganticApacheWebServer) ; 25 Spaces Around Operators and After Commas DO my %server_list = (1030=>”rs1”, 1031=>”rs2” ); DO NOT my %server_list=(1030=>”rs1”,1031=>”rs2” ); 26 Regular Expression DO / ( [a-fA-F0-9] ) - [a-fA-F0-9]{4} - [a-fA-F0-9]{4} - [a-fA-F0-9]{4} - [a-fA-F0-9]{12} /x DO NOT /([a-fA-F0-9])-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/ Also, Don’t Overdo Regex 27 Compile with “Strict” script compile <script file name> strict Or include in the OpenScript code use strict; use Sub::StrictDecl; …………………………… ……………………………. ADX(config)#script compile hello.pl This script is compiled successfully. Performance for this script: - Approximately 5200 connections/second at 10% CPU utilization - Approximately 26200 connections/second at 50% CPU utilization - Approximately 52400 connections/second at 100% CPU utilization ADX(config)#script compile hello.pl strict S_my_exit_jump being called Compile Errors found : Undeclared subroutine &OS_SLB::Forward at -e line 11. BEGIN not safe after errors--compilation aborted at -e line 17. * the strict option in the compilation CLI and Sub:StrictDecl require 12.4.00f or later. 28 SLB Forwarding API Use OS_HTTP_REQUEST () { if ( EXPR ) { OS_SLB::forward(1); } OS_SLB::forward(2); } OS_HTTP_REQUEST () { if ( EXPR ) { OS_SLB::forward(1); } else { OS_SLB::forward(2); } } Wrong 29 No Object Oriented Convention DO $url = OS_HTTP_REQUEST::url; DO NOT $req_obj = OS_HTTP_REQUEST::get; $url = $req_obj->url Not Supported, starting in 12.4.00e for increased performance by 30% 30 BEGIN Subroutine my %network_group; my $net_mask_hex; sub BEGIN { my $net_mask = "255.255.0.0"; %network_group = ( "10.1.0.0“ => "GROUP_A“, "10.2.0.0“ => "GROUP_B”, “10.3.0.0“ => "GROUP_C" ); my @net_mask = split ('\.', $net_mask); $net_mask_hex = pack(C4, @netmask); • Do not assign a value to a variable outside a sub routine • Just declare the variable with the scope“my” outside a sub routine as lexical scoping is required. Do As Many As Operations in the BEGIN } my $ip = OS_IP::src; my @ip = split ('\.', $ip); my $net_hex = pack ("C4", @ip) & $net_mask_hex; Main Event Handler $net = join (‘.', unpack("C4", $net_hex) ); $group_name = $network_group { $net }; 31 Print Statement and Log script-profile production print-output none ! server virtual v1 10.24.142.86 port http port http script "hello.pl" script-profile "production” print: Do not use in production except for debugging purpose OS_SLB::log: Use to log unexpected and rare events in production environment 32 Use Memory To Save CPU sub HTTP_REQUEST { my $host = OS_HTTP_REQUEST::host; my %host_server_mapping; } elsif ( $host =~ m/www.host2.com ) { OS_SLB::forward (rs2); sub BEGIN { %host_server_mapping = { “www.host1.com”=>”rs1”, “www.host2.com”=>”rs2”, “www.host3.com”=>”rs3”}; } } elsif { $host =~ m/www.host3.com ) { OS_SLB::forward (rs3); sub HTTP_REQUEST { my $host = OS_HTTP_REQUEST::host; if ( $host =~ m/www.host1.com/) { OS_SLB::forward (rs1); my $server = $host_server_mapping {$host}; } else { OS_SLB:reset_client(); } if ( $server ) { OS_SLB::forward($server); } else { OS_SLB:reset_client(); } } } 33 Release Memory After Use my %url_hash = (); sub HTTP_REQUEST () { my $req_id = OS_CONN::client_connection; $url_hash {$req_id} = OS_HTTP_REQUEST::url; } sub HTTP_RESPONSE () { my $req_id = OS_CONN::client_connection; print “url from the corresponding request is $url_hash{$req_id} \n”; delete $url_hash{$req_id}; } 34 Trouble Shooting Where To Start • Is my script attached to a VIP? • Did the script modification take into effect? • Do event counters increase? show script <script name> program • Does my script reset? show script <script name> detail <vip name> <port> • Is there any typo in OpenScript APIs? script compile <script name> strict • Is the cause of a problem not identified? script update <script name> use “print” in the script url debug 3, debug filter, packet capture 35 Trouble Shooting What To Gather To Escalate • Problem description • show run • show script <script name> program • show script <script name> detail <vip name> <port> • Gather 2~3 times after issuing “script update” to reset counters. • url debug 3 <client ip> • packet trace from your client PC 36 Trouble Shooting Example My OpenScript code does not send a sorry page out though all servers are down. ADX#show server bind Bind info Virtual server: test Status: enabled IP: 20.1.1.1 ADX(config)#script compile sorry.pl strict http -------> rs1: 10.10.1.10, http (Failed) show script sorry.pl program This script is compiled successfully. rs2: 10.10.1.1, http (Failed) Performance for this script: use OS_SLB; - Approximately 4100 connections/second at 10% CPU utilization use OS_HTTP_REQUEST; - Approximately 20600 connections/second at 50% CPU utilization show run - Approximately 41200 connections/second at 100% CPU utilization my $page; ADX#show script sorry.pl detail v1 80 server real rs1 10.1.1.10 =============================================== port 80 group-id 10 10 sub BEGIN { Virtual Server: v1 $page = "HTTP/1.1 200 OK\r\nContentService-Port: http server real rs2 10.1.1.11 Length:21\r\nConnection:close\r\nContent- Script State: ACTIVE port 80 group-id 10 10 Type: text/html; charset=UTF-8\r\n\r\n. Last Updated: 00:59:21, GMT+00, Sun Mar 3 2013 Sorry .Get. back later.”; server virtual v1 20.1.1.1 } Script Restart: 0 port 80 script “sorry.pl” bind 80 rs1 80 rs2 80 sub HTTP_REQUEST { Total Connections: 0 OS_SLB::forward (10); Concurrent Connections: 0 } Error Counters: sub SERVER_SELECTION_FAILURE { exceed max rewrite entry: OS_SLB::reply($page); } Hits Per Event: HTTP Request event: server l7-dont-reset-on-vip-port-fail 37 THANK YOU 38