6
\$\begingroup\$

Your program reads from standard input and prints to standard output.

The first thing you'll be receiving on stdinput will be the ruleset for your firewall (until you encounter a double newline).

We'll start explaining the ruleset itself with some sample input:

/* header abbreviations added for demonstration purposes only */

 /> Allow (A) or Block (B)
|
|  /> Source (incl. mask)
| |
| |               /> Destination (incl. mask)
| |              |
| |              |          /> Source port(s)
| |              |         |
| |              |         |           /> Destination port(s)
| |              |         |          |

A 192.168.1.0/24 0.0.0.0/0 1024:65536 80
A 0.0.0.0/0 192.168.1.0/24 80 1024:65536
A 192.168.1.1/32 0.0.0.0/0 80 1024:65536
A 0.0.0.0/0 192.168.1.1/32 1024:65536 80
B 0.0.0.0/0 0.0.0.0/0 * *
  • Rules are processed in cascading order. Once you find a matching rule, determine the outcome and stop processing.
  • If a packet does not match any given rule, block it (sane defaults).
  • Elements of a rule are single-space delimited.
  • Source and destination adresses will provide a netmask. Be sure to filter accordingly.
  • The specification of the source and destinations ports must support the following syntax:
    1. Single ports: e.g. "80"
    2. Inclusive port ranges: e.g. "1024:65536"
    3. All ports: e.g. "*"
  • You may assume input to be well-formed.

After the double newline, you'll receive (one or more) newline seperated input packets to test according to the ruleset you've now acquired.

Some sample input for the packets to clarify:

/* header abbreviations added for demonstration purposes only */

 /> Source
|
|             /> Source port
|            |
|            |     /> Destination
|            |    |
|            |    |              /> Destination port
|            |    |             |

192.168.1.18 1036 157.136.122.5 22
157.136.108.8 2400 192.168.1.1 80

You'll examine these input packets and determine the outcome of filtering according to the rules.

The above sample input (ruleset + packets) should thus yield the following output:

BLOCK 192.168.1.18 1036 157.136.122.5 22
ALLOW 157.136.108.8 2400 192.168.1.1 80

The output is basically no more than 'BLOCK' or 'ALLOW' followed by a space and the verbatim input line.

Some varia:

  • This is code golf, the shortest (source code, by byte count) solution wins. Have fun!
  • You are not allowed to use specific IP address handling features of your language.
  • Specialized languages for routing or firewalling will be excluded from contending for first place (if they exist).
  • If any part of the question is ambiguous or in need of further clarification, feel free to comment.
\$\endgroup\$
0

3 Answers 3

3
\$\begingroup\$

Haskell, 356 characters

main=interact$f.break null.lines;f(r,_:p)=unlines$map(foldr(g.w)(j"B"++)r)p
g[r,s,d,a,b]n x|and$zipWith3($)(cycle[k.('/'#),q.(':'#)])[s,a,d,b]$w x=j r++x|t=n x
k[y,z]x=y%z==x%z;y%z=foldl((.r).(+).(*256))0('.'#y)`div`2^(32-r z)
q["*"]x=t;q[y]x=x==y;q[a,b]x=r x>=r a&&r x<=r b
d#x=w$map(d?)x;d?x|d==x=' '|t=x;j"A"="ALLOW ";j"B"="BLOCK ";t=1<3;r=read;w=words
\$\endgroup\$
1
\$\begingroup\$

Perl 561

First try. Any tips?

[gary@phoenix ~]$ cat test | perl f2.pl
BLOCK 192.168.1.18 1036 157.136.122.5 22
ALLOW 157.136.108.8 2400 192.168.1.1 80


sub r{($i,$j)=split'/',shift;$j eq 0?'*':map$h.=sprintf("%.8b",$_),split/\./,$i;
substr$h,0,$j}sub s{$_[0]=~/(\d+):(\d+)/?$1<$_[1]&& $_[1]<$2:$_[0]==$_[1]}sub
t{chomp;($a,$b,$c,$d,$e)=split/ /}sub o{$_[0]eq'*'or$_[0] eq
substr&r($_[1].'/32'),0,length$_[0]}sub p{$_[0]eq'*'or&s($_[0],$_[1])}
map((/^[AB]/?(t,push@f,join' ',($a,&r($b),&r($c),$d,$e)):push@g,$_),<STDIN>);
push@f,"B * * * *\n";for $q(@g){if($q=~/[0-9]/){($k,$l,$m,$n)=split/ /,$q;
for(@f){&t;$a=$a=~/^A/?'ALLOW':'BLOCK';&o($b,$k)&& &o($c,$m)&& &p($d,$l)&&
&p($e,$n)?print"$a $q":next;last}}}
\$\endgroup\$
8
  • 2
    \$\begingroup\$ I'm not familiar with Perl, but at first glance, I see lots of duplicate code, like ($a,$b,$c,$d,$e)=split(/ /); and (($b eq '*') || $b eq substr(&r($k.'/32'),0,length($b))). Try factoring those into loops or functions. \$\endgroup\$
    – mellamokb
    Commented Feb 15, 2012 at 17:38
  • 2
    \$\begingroup\$ A lot of the whitespace can be removed. ...?return 1:return 0 seems to cry out for return ...?1:0 or even return ... if that works. (My knowledge of Perl is quite superficial). \$\endgroup\$ Commented Feb 15, 2012 at 20:23
  • 1
    \$\begingroup\$ I believe you can remove the whitespace around ==, ||, ? and :; and the whitespace in $q (, and before all "s. \$\endgroup\$
    – Ry-
    Commented Feb 17, 2012 at 4:22
  • 1
    \$\begingroup\$ From a quick glance: 1) most ;} sequences could be rewritten as just } 2) don't call functions as &o, go for straight o 3) a lot of function-call-parentheses can be dropped (for user-defined functions, move the definition somewhere before the call) 4) a lot (all?) of returns can be dropped. \$\endgroup\$
    – J B
    Commented Feb 21, 2012 at 22:14
  • 1
    \$\begingroup\$ r might be a case where you can't do it, since return was being used to short-circuit the rest of the function body. \$\endgroup\$
    – J B
    Commented Feb 23, 2012 at 8:00
1
\$\begingroup\$

Ruby (384)

Not sure it's 100% correct, but works at least for the given example inputs. Propably could be made a bit shorter too.

$ wc gc4907-firewall.rb
       9      15     384 gc4907-firewall.rb

$ cat fwinput| ruby gc4907-firewall.rb
BLOCK 192.168.1.18 1036 157.136.122.5 22
ALLOW 157.136.108.8 2400 192.168.1.1 80
q=->x{x.gsub(/(\d+)\.?/){'%02x'%$1}.hex}
w=->x{u=x.scan(/[\d.]+\/\d+/)
p=$'.split.map{|z|z=="*"?->y{1}:(z=eval z.sub(?:,'..');->y{z===y.to_i})}
f=u.map{|z|a,m=z.split ?/;a=q[a]>>(m=32-m.to_i);->y{a==q[y]>>m}}.zip(p).flatten
->y{f.zip(y.split).all?{|z,c|z[c]}&&x[0]}}
s=[]
while gets=~/./;s<<w[$_];end
s<<->x{0}
$<.map{|p|s.map{|f|(f=f[p])&&(puts (f=="A"?"ALLOW ":"BLOCK ")+p;break)}}

$' seems to confuse both brace matching in emacs, and syntax highlight here.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.