Here's the code: XML routing

Linux Advanced Routing and Traffic Control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I've put together scripts to save the routing tables in text or xml form.

This message explains
1. how they are used
2. the approach I have taken
3. what is needed next.

This is related to other work which converts iptables to xml and back
(now in iptables 1.3.7), htb files into xml and xml to tc commands
(complete but not yet released) and ebtables to xml and back (to be
released any day now).

I find that having iptables, ebtables, tc and ip route as xml permits
some very useful manipulations.

This first draft is thorough, but maybe not correct, so I welcome
feedback; please see STRATEGY further down:

I've made use of bash's <( ) to package some external resource files
(large sed script and xslt) in the same bash script so there is only one
 file to distribute and try out.

You will need xsltproc to restore xml routes

Basic Usage
===========

iproute save [-x][-t][-s] [filename]xml

will save the rules and routes in text [-t] format (default), xml [-x]
format, and with [-s] or without the /proc/sys/net/ipv4/route routing
paramters. The save is atomic.

xml
iproute restore [-x][-t][-s] [filename] behaves similarly.

If iproute restore fails to restore the routes, it attempts to rollback
to the previous set of routes.

Conversion to xml is managed with sed; conversion from xml requires
xsltproc, a command line C based xslt 1.0 processor.


iproute flush

will pretty much flush the rules and routes; this is also done before
restore or rollback. See Strategy.

Formats - TEXT
--------------

The text format is roughly the same as:
( ip -o rule show ; ip -o route show table all )
but has a notation like:
> error_burst 1250
for the sysctl paramters; which are converted to
echo 1250 > /proc/sys/net/ipv4/route/error_burst

Formats - XML
-------------

The xml format is along these lines:
<ip-route version="1.0"  error_burst="1250" gc_elasticity="8"  ... >
  <rules>
    ...
    <rule priority="298" from="11.22.111.222" table="222"/>
    ...
  </rules>
  <tables>
    <table id="222">
      <route prefix="1.1.0.0/8" mtu="100" metric="40">
        <next-hop via="192.168.0.23" dev="eth3" weight="2"/>
        <next-hop via="10.1.1.1" dev="eth4" weight="3" type="onlink"/>
      </route>
      ...
    </table>
    ...
  </tables>
</ip-route>


Stategy
=======

Flushing
--------

Before applying routes and rules (or rolling back previous routes and
rules on failure) some kind of routing table flush is needed.

The difficulty I am under is knowing what should be flushed and what
should not.

I guess if someone is using a routing daemon, they won't be using this -
although I could be wrong; thus I only need to be careful where proto =
kernel, or I can be liberal where proto = boot (or not specified).
As the kernel only fiddles with routes in the main and default tables, I
don't bother to check proto generally.

Also I don't want to fiddle with the local table.
I've decided not to fiddle with the default table (is this right?)
I empty every other table totally, except the main table where I
preserve routes with scope=link, as to my dull mind messing with these
would be silly.

I delete all rules except 0, 32766 and 32767

So, yes, I also delete the default gateway.

If someone also wanted to use this on a system with a routing daemon
running, we should probably pay more attention to the proto parameter; I
would need to talk with someone who knows more about mixing routing
daemons with non-daemon routes.

I flush tables using the "ip route flush" command.

Parsing / Restoring
-------------------

I flush or restore rules by some sed which converts the initial priority
and colon to the keyword priority; combined with the rest of the output
these seems parsable as input.

similarly for tables, prefixing route add or route delete to the results
of a line from: ip -o route show table all
 seems a good way to delete or restore a route individually.

dev entry
---------

When restoring routes it seems prudent to remove the dev entry for any
via unless it is an onlink route; for the reason that generally the dev
entry was not specified initially but divined from the device tables and
netmasks and such, and it would be a good idea to let ip route do this
again in case the interface addressing has changed.

Currently the dev entry is removed during xml restore and not text
restore.  It should probably be removed during save, so that it may be
specified strictly as part of a fresh xml routing profile without being
ignored.

This would have the disadvantage that when save were used for diagnostic
or informational purposes the dev information would be missing.

Local Main Default
------------------

Likewise, for informational reasons, the default, local and main table
(and their rules) are saved; but skipped when a restore takes place.
It is presumed that whatever set these tables up in the first place (and
their rules) has done the same again.

The only exception is that routes for default are processed if they have
no scope (text restore) or they are default routes (xml restore). This
difference is historical and should be removed.

Converting to XML
-----------------
I've written a few scripts to convert from various text formats to xml.
This one was the most fun yet. I do rely on the fact that ip route spits
out simple text that does not need any xml escaping (my other
conversions do xml escaping properly).

Because the output of ip route is structured fluidly (the via
information appears in the middle of the per-route information unless
there is more than one hop) and because I'm not entirely sure of all the
outputs of ip route, I've structured the xml conversion to detect when
unrecognized parameters exist, and output xml comments to warn of this.today

Those who have used sed heavily will know it only has 2 variables: the
"current" line, and a "spare". I've made cunning use of these in ways
that will be hard to grasp and easy to break if you don't know sed well.
It's not as spaghetti as it looks, I have had do develop certain idioms
for converting to xml and once you spot these a new macro-level
understanding will coalesce making the whole thing easier to understand.

Why did I use sed seeing how complicated it is? I've learned that using
bash to convert xml is a SLOW mistake, and I really don't want to
introduce dependancies on anything even as big as awk - which probably
would have done a better job (what with having more than two variables,
and hashes and all!). So what about xsltproc? libxslt and xsltproc are
smaller than gawk. Also, I was learning sed for fun and wanted to see
how far I could push it.

However, Generating XML is generally simple; if what I have done is
worthwhile, it will be simple enough to get the ip command to output xml
as an alternative format.

Converting from XML
-------------------

I've been generating bash-able output from XML for quite some time and
developed a full xslt library of escape functions to prevent injection
errors from crafted xml.

For this project, and to keep the xslt dependancies simple, I've tried a
new strategy which is to not generate bash script lines and execute
them, but instead to validate the output in bash and use them as
parameters instead of commands, reducing the scope for naughtiness.

Because the xml saving was done before the text saving, the xslt
generates ip route commands directly instead of the text format and then
using the text format conversion.  The asymmetry is interesting but I
don't plan to do anything about it.

Next Steps
==========

This work suits me and my employer, but we don't see why we should be
the only ones to benefit. Our benefit is not in having xml routes, but
how we process the xml.

Those who have an interest, please examine this and see how useful it is.

I have a source-rpm as well which I will distriute once we've reviewed
this once.
#! /bin/bash

# iproute - save and restore ip route's in text or xml format
# Copyright (C) UFO Mechanic <azez@xxxxxxxxxxxxxxx>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the:
#  Free Software Foundation, Inc., 
#  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# Severe error means we left the system in an unknown state
# probably with partial routing. It means the new routes wouldn't
# go in and we couldn't restore the old ones
ERROR_SEVERE=4

# Mild errors means we didn't do what we were asked, but as far as
# we can tell it was a clean failure and nothing is damaged

# System error means basic things failed, like creating files in /tmp
# If the environment isn't useful, there's not much we can do.
# System errors are mild
ERROR_SYSTEM=3
# XML error means the xml processor failed, probably bad xml
ERROR_XML=2
# ROUTE error means that ip would not process the route commands,
# perhaps bad xslt or good xml specifying a bad configuration
ERROR_ROUTE=1

SYSCTL_ATTR="error_burst gc_elasticity gc_min_interval_ms max_delay min_delay redirect_load secret_interval error_cost gc_interval gc_thresh max_size min_pmtu redirect_number gc_min_interval gc_timeout min_adv_mss  mtu_expires redirect_silence"
SYSCTL_PATH="/proc/sys/net/ipv4/route"
IPROUTE_TMP=/tmp/iproute-save.$$
SYSCTL=""
FORMAT="text"

# clean up temporary files
on_exit() {
  rm -f /tmp/rollback.$$ /tmp/route.$$ $IPROUTE_TMP
}

trap on_exit 0

# validate echo nnn > /proc/sys/net/ipv4/route/*
# and execute according to PERMIT_SYSCTL
ip_sysctl() {
  if [ "$1" = "echo" -a "$3" = ">" -a "$#" = 4 ]
  then
    case "$4" in
      *..*) ;; # escape any dodgy paths
      /proc/sys/net/ipv4/route/*) # it's legal, is it permitted?
          if [ -n "$SYSCTL" ]
          then echo "$2" > "$4" || return $ERROR_ROUTE
          fi
          return ;;
    esac
  fi 
  echo "Bad set route paramter: $*" >&2 
  return $ERROR_ROUTE
}

# Rather than pipe output xsltproc output directly to a shell,
# we avoid shell injection, and inspect each line (read from stdin)
ip_run_commands() {
  while read ip_line
  do
    set -- $ip_line
    case "$1" in
      ip) "$@" || return $?;;
      echo) ip_sysctl "$@" || return $?;; 
      '#'*|'') ;;
      *) echo "Invalid IP command: $*" >&2
         # It's severe until we rollback
         return $ERROR_SEVERE;;
    esac
  done
}

# convert ip show/save format to shell commands. 
# also handles systl's denoted by >
ip_save_to_commands() {
  sed -e '/^[0-9]*:/{ # its a rule
            /^0:/d;/^32766:/d;/^32767:/d;
            s/ none *$/ 0/;s/^\([^: \t]*\)[:\t ]*/ip rule add pref \1 /;t;d
          }
          /^> /{ # its a sysctl
            tSYSCTL
            :SYSCTL
            s/^>  *\([^ ]*\)  *\(.*[^ ]\) *$/echo \2 > \/proc\/sys\/net\/ipv4\/route\/\1/
            tSYSCTL_OK
            s/^/#/
            :SYSCTL_OK
            b
          }
          # must be a table
          /  table local /d;
          /  table default /d;
          # ignore non-global scope for main
          /  table /!{ 
            /  scope /d
          };
          /  table main /{ 
            /  scope /d
          };
          s/\\\t/ /g;
          # fix bad rtt suffixes
          s/\( rtt[^ ]* [0-9]*\)[^ ]*/\1/g; s/^/ip route add /'
}

# read rules in a way ready to do ip rule add, ip rule delete on
# "$1" is inserted to the beginning of the line
# Ignores rules 0, 32766 and 32767
ip_read_rules() {
  ip rule show | sed -e '/^0:/d;/^32766:/d;/^32767:/d;s/ none *$/ 0/;s/^\([^: \t]*\)[:\t ]*/'"$1"' pref \1 /;t;d'
}

# read routes in a way to do ip route add or ip route delete on
# "$1" is inserted at the beginning of the line
# Ignore default and local and scope links in main
ip_read_routes() {
  # here we are relying on the double space that occurs before the keyword table and the single space that always follows the table name
  # We also remove the ms suffix from the rtt and rttvar parameters.
  # BUG: route show displays a smaller rtt than was set, so each rollback shrinks rtt. rttvar does not have this problem  
  ip -o route show table all | \
    sed -e '/  table local /d;/  table default /d;/  table /!{/  scope /d};s/\\\t/ /g;s/\( rtt[^ ]* [0-9]*\)[^ ]*/\1/g; s/^/'"$1"' /'
}

# output table names apart from main, local, default
ip_show_tables() {
  ip -o route show table all | \
    sed -ne '1{x;s/^.*/ main local default 255 254 253 /;x;tDone;:Done;};s/^.* table  *\([^ ]*\).*/\1/;T;G;/^\([^\n]*\)\n.* \1 /!{P;s/\n/ /;s/^/ /;x;}'
}


# Flush existing rules
ip_flush_rules() {
  ip_read_rules "ip rule del" | ip_run_commands
}

# Flush existing tables entirely (except local, main, default)
# Of main, flush non local scope rules. Local scope rules seem to
# refer to per-local-ip routes
ip_flush_routes() {
  ip_show_tables | \
  while read table
  do
    ip -o route flush table $table
  done

  # Flush non link scope'd rules from main - call that scope global for now
  ip route del table main scope global &>/dev/null
}

# produce a set of ip commands which will restore a newly
# flushed  routing table to it's current state
ip_prepare_rollback() {
  save_text
}

flush() {
  # perhaps we should only delete proto boot routes (and only scope globan for table main)
  ip_flush_routes
  # ..and perhaps we should only delete rules whose tables are now empty?
  ip_flush_rules
}

ip_rollback() {
  flush
  ip_save_to_commands < /tmp/rollback.$$ | ip_run_commands
}

restore_text() {
  ip_save_to_commands < "$FILE" > /tmp/route.$$ || return $ERROR_ROUTE
  ip_run_commands < /tmp/route.$$ || return $?
}

# generate ip commands, don't execute unless we do this successfully
restore_xml() {  
  xsltproc <( iproutexml_xslt ) "$FILE" > /tmp/route.$$ || return $ERROR_XML
  ip_run_commands < /tmp/route.$$ || return $?
}

save_text() {
  test -n "$SYSCTL" && for attr in $SYSCTL_ATTR
  do
    read f < "$SYSCTL_PATH/$attr"
    echo "> $attr $f"
  done
  ip -o rule show
  ip -o route show table all
}

save_xml() {
  save_text | iproute2xml
}

save() {
  test -n "$1" && IPROUTE_TMP="$1.$$"
  if save_$FORMAT > "$IPROUTE_TMP"
  then 
       if [ -n "$1" ]
       then mv "$IPROUTE_TMP" "$1" || exit 1
       else cat "$IPROUTE_TMP"
       fi
  else
    exit 1
  fi
}

restore() {
  FILE="${1:-/dev/stdin}"

  if ! ip_prepare_rollback > /tmp/rollback.$$
  then echo "Can't prepare rollback file" >&2
       return $ERROR_SYSTEM
  fi

  flush
  # apply xml routes or rollback
  if restore_$FORMAT "$@"
  then # flush routing cache
       ip route flush cache
  else # need to roll-back previous route
       echo "Attempting to restore previous routing table" >&2
       if ! ip_rollback 
       then echo "Rollback failed!" >&2
           return $ERROR_SEVERE
       fi
       return $ERROR_ROUTE
  fi
}

help() {
  case "$0" in
    *save) ;;
    *restore) ;; 
    *) action=" [save|restore]";;
  esac
  
  cat<<HELP
Usage: $0 [-s][-x]$action [filename]
  -x        File in xml format
  -x        File in text format (default)
  -s        Also restore sysctl settings (if any) to $SYSCTL_PATH
  filename  The name of the route save file, defaults to stdin/stdout
HELP
  exit
}

main() {
  while getopts "htxs" Option
  do
    case $Option in
      s) SYSCTL=1;;
      x) FORMAT=xml;;
      t) FORMAT=text;;
      h) help;;
      *) echo "Unknown Option: $Option $OPTARG" >&2 ; help >&2; exit 1;;
    esac
  done
  shift $(($OPTIND - 1))

  case "$0" in
    *save) save "$@";;
    *restore) restore "$@";; 
    *) "$@";;
  esac
}

# This was a standalone sed script, but I built it into bash for fun and ease of initial distribution
iproute2xml() {
  sed -n -f <( iproute2xml_sed )
}

iproute2xml_sed() {
cat<<'ENDOFSED'
#! /bin/sed -nf

# invoke like this:
#   ( ip -o rule show ; ip -o route show table all ) | ./iproute2xml
# or
#   ( echo 'echo attr="value" attr2="value2"' ; ip -o rule show ; ip -o route show table all ) | ./iproute2xml
#   to have the attributes inserted into the outer node; e.g.
#   e.g.
#   ( echo "echo 2 > /proc/sys/net/ipv4/route/error_bust"; ip -o rule show ; ip -o route show table all ) | ./iproute2xml

1{ # if it's a "special" attributes line merge it with the open tag
  tOpenTag
  :OpenTag
  /^> /{ # looks like a sysctl routing param
    x
    s/^.*/<ip-route version="1.0"/
    x
    tMakeAttr
    :MakeAttr
    s/^>  *\([^ \/]*\)  *\(.*[^ ]\) *$/\1="\2"/
    TSkipAttr
    H
    :SkipAttr
    n
    /^> /bMakeAttr
    # thats all attributes done
    x
    s/\n/ /g
    s/$/>/
    p
    s/^.*//
    x
    bDoneOpenTag
  }
  :StaticOpenTag
  i<ip-route version="1.0" \/>
  :DoneOpenTag
}
${# close dangling tags
  x
  /^:/a\ \ </rules>
  /^[^:\n]/{
    a\ \ \ \ </table>
    a\ \ </tables>
  }
  a</ip-route>
  x
}
# line 1 of h will contain the current table, or : if we are processing rules
/^[0-9][0-9]*:/{# rules...
  x
  /^:/!{# open rules tag
    i\ \ <rules>
  }
  s/^.*//
 
  # keep copy of line for error reporting
  g
  x
  
  # now process input line itself. 
  # leave un-recognized fragments on line 2 so we can warn about them
  s/^\([0-9][0-9]*\):[\t ]*/    <rule priority="\1"\n /
  # get rid of some default values
  s/\n\(.*\) from all */\n\1 /
  # recognize known attributes and values
  tPullRuleAttributes
  :PullRuleAttributes
  s/\n\(.*\) \(from\|to\|iif\|tos\|fwmark\|realms\|nat\) \([^ ]*\)/ \2="\3"\n \1/
  tPullRuleAttributes
  
  s/\n\(.*\) lookup \([^ ]*\)/ table="\2"\n \1/
  s/\n\(.*\) \(blackhole\|prohibit\|reject\|unreachable\|unicast\|none\|[0-9][0-9]*\)/ type="\2"\n \1/
  s/\n/\/>\n/
  
  # anything left on line 2 is unrecognized attributes of the rule
  /\n *[^ ]/{
     # save line 2 on to the end of holding space as warning
     s/\n */\n         Warning; Unknown ip rule arguments: /
     G
     h
     s/[^\n]*\n\([^\n]*\)\(.*\)/\2\n\1/
     s/^\n* *//
     x
  }
  # strip line 2
  s/\n.*//

  # print output, comments first, if any...
  x
  ## ADJUST: Un-comment this line to stop input appearing as output comment unless there is an error
  /\n/!s/^.*//
  
  /^./{
    s/^/\n    <!-- /
    s/$/ -->/
    p
  }
  x
  p
  
  # note that we have an open rule tag
  s/^.*/:/
  x

  d
}

# soo... it must be a table entry
{
  # holding space line 1 is used to hold currently open table name
  # holding space line 2 and onwards will be for xml comments and errors
  x
  s/\n.*//
  # keep copy of input for error reporting
  G
  x

  # normalize multi-hop lines into multiple lines
  s/\\\tnexthop /\n /g

  tStartRoute
  :StartRoute
  s/^ *\(via\|anycast\|unicast\|local\|broadcast\|multicast\|throw\|unreachable\|prohibit\|\blackhole\|\nat\)  *\([^ ]*\)/      <route prefix="\2" type="\1"\n/
  tDonePrefix
  s/^ *\([^ ]*\)/      <route prefix="\1"\n/

  :DonePrefix
  # line 1 has start of tag, type (if any) and prefix
  # line 2 has unrecognized tags
  # line 3 has explicit hop statements which dont contain any NODE_SPEC
  # so we only need to pull NODE_SPEC from line 2. 
  # If there is a line 3 then anything left in line2 is unknown, otherwise line 2 may contain NODE_SPEC and unknown INFO_SPEC mixed

  # gather infospec fragments from lines 2 onwards
  # look for mtu-lock specially
  s/^\([^\n]*\)\n\(.* \)\(mtu lock\)  *\([^ ]*\)/\1 mtu="\4" mtu-lock="yes"\n\2/

  tPullNodeSpec # reset t command
  :PullNodeSpec
  # of course mtu\|advmss\|rtt\|rttvar\|window\|cwnd\|ssthresh\|realms only make sense if there are vias
  # but we dont check that here
  s/^\([^\n]*\)\n\([^\n]*  *\|\)\(tos\|reordering\|proto\|scope\|metric\|src\|preference\|mtu lock\|mtu\|advmss\|rtt\|rttvar\|window\|cwnd\|ssthresh\|realms\)  *\([^ \n]*\) */\1 \3="\4"\n\2 /
  tPullNodeSpec
  # before we split on via, move table attribute to beginning of line 2
  s/^\([^\n]*\)\n\([^\n]*\) table  *\([^\n ]*\)/\1\ntable \3\2/

  # if there is a via on line 2 then there are not multiple hops, so split a line 3
  # although there could still be some unknown INFO_SPEC on the end.
  # thus we keep a dev with its preceeding via if we can
  tSplitVia
  :SplitVia
  s/^\([^\n]*\n[^\n]*\) \(via\) /\1\n via /
  # if this works we need to pull the table entry (if any) from the new line 3 back to line 2

  # now with all recognized node-spec from line 2,
  # if we have 1 dev entry in line 2, (and no vias), we will pull it into main node
  # There wont be a dev on line 2 if we have multiple hops anyway
  # Also this depends on the fact that dev appears after a via
  tPullDev # reset t command
  :PullDev
  s/^\([^\n]*\)\n\([^\n]*\)\(dev\)  *\([^ ]*\)/\1 \3="\4"\n\2/
  tPullDev

  # whatever is left in line 2 we did not recognize as NODE_SPEC even though it occured before INFO_SPEC
  # the table declaration should also be in line 2, which we will now recognize
  # if there is no table declaration then it is table main which we will fake
  /^[^\n]*\ntable  *[^\n ]/!s/\n/\ntable main /

  # now see if the current table is the same as the active table
  # ASSERT: The hold space must have exactly 2 lines in it
  H
  x
  /\([^\n]*\)\n[^\n]*\n[^\n]*\ntable \1[ \n]/!{
    # different table!
    # do we need to close a rules tag?
    /^:/i\ \ </rules>
    # do we need to open a tables tag?
    /^:*\n/i\ \ <tables>
    # do we need to close a table tag?
    /^[^:\n]/i\ \ \ \ </table>
    # get rid of table tag
    s/[^\n]*\n\([^\n]*\n[^\n]*\ntable  *\([^ \n]*\)\)/    <table id="\2">\n\2\n\1/
    P
    # strip tag
    s/^[^\n]*\n//
  }        
  # and strip current line so we just have table name and original line again
  s/\n\([^\n]*\).*/\n\1/
  x

  # combine the table tag from line 2 with line 1
  s/^\([^\n]*\)\ntable  *\([^\n ]*\) */\1\n/

  # anything left in line 2 is unrecognized NODE-SPEC. 
  {
    H
    x

    tNodeSpecWarning
    :NodeSpecWarning
    # Copy unknown node-spec to line 3
    s/^\([^\n]*\)\n *\([^\n]*\n\)[^\n]*\n\([^\n][^\n]*\).*/\2           Warning; Unknown NODE-SPEC attributes: \3 \n\1/
    tDoneNodeSpecWarning
    # There was no warning needed, copy a second line instead of warning
    s/^\([^\n]*\)\n *\([^\n]*\n\).*/\2\n\1/
    
    :DoneNodeSpecWarning

    # And if there is no warning, remove the input line unless it is wanted anyway
    # ADJUST: Comment-out this line to show input lines even if no error
    s/^\([^\n]*\)\n\n\(.*\)/\n\n\2/

    s/ *\n *\n */\n/
    
    /^[^\n].*\n/{ # is there anything to be said?
      s/^/\n      <!-- /
      s/^\(.*\)\n/\1 -->\n/
      # Need to print everything except last line; 
      :NodeSpecWarningPrint
      P
      s/^[^\n]*\n//
      /\n/bNodeSpecWarningPrint
    }
    s/.*\n//
    x
  }
  # ASSERT holding space has only 1 line again which is the table name

  # close the tag on line 1
  s/\n/>\n/
  # if we dont have anything else to analyse then self-close the tag
  # is there a none-space character after line 2?
  tclosing
  :closing
  /^[^\n]*\n[^\n]*\n.*[^ \n]/!s/>\n/\/>\n/
  P
  tend

  # we have to output the different hop tags, chop off first line
  s/^[^\n]*\n//

  tvia # reset t command
  :via

  # and empty the second line
  s/^[^\n]*//

  # if there is nothing left we are done...
  /^\n./!bdonevia
  
  # chop off previous line
  s/[^\n]*\n//
  
  # remember current line for warnings
  H
  x
  s/$/\n/
  # Store line after line 1 table name
  s/\([^\n]*\n[^\n]*\).*/\1/
  x
 
  # off we go...
  s/^/        <next-hop\n /
  tPullNextHop
  :PullNextHop
  s/^\([^\n]*\)\n *\([^\n]*\) \(via\|dev\|weight\)  *\([^ \n]*\)/\1 \3="\4"\n \2/
  tPullNextHop
  
  # NH flags
  s/^\([^\n]*\)\n\([^\n]*\)  *\(onlink\|pervasive\)\( .*\|\n.*\|\)$/\1 type="\3"\n \2 \4/

  # close the tag
  s/ *\n/\/>\n/

  # if there is anything left in line 2 we need to raise a warning
  {
    H
    x

    tHopSpecWarning
    :HopSpecWarning
    # Copy unknown node-spec to line 3
    s/^\([^\n]*\)\n *\([^\n]*\n\)[^\n]*\n *\([^\n ][^\n]*\).*/\2             Warning; Unknown NEXT-HOP attributes: \3\n\1/
    tDoneHopSpecWarning
    # There was no warning needed, copy a blank line
    s/^\([^\n]*\)\n *\([^\n]*\n\).*/\2\n\1/
    :DoneHopSpecWarning
    
    # And if there is no warning, remove the input line unless it is wanted anyway
    # ADJUST: Comment-out this line to show input lines even if no error
    s/^\([^\n]*\)\n\n\(.*\)/\n\n\2/

    s/ *\n *\n */\n/
    
    /^[^\n].*\n/{ # is there anything to be said?
      s/^/\n        <!-- /
      s/^\(.*\)\n/\1 -->\n/
      # Need to print everything except last line; 
      :HopSpecWarningPrint
      P
      s/^[^\n]*\n//
      /\n/bHopSpecWarningPrint
    }
    s/.*\n//
    x
  }
  P
	s/^[^\n]*\n//
  bvia

  :donevia
  i\ \ \ \ \ \ </route>
  :end
}
ENDOFSED
}

iproutexml_xslt() {
  cat<<'ENDOFXSLT'
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Converts from the ufomechanic simple ip route xml format to ip route commands
     that could be executed by a shell or shell wrapper
 -->
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform";>
  <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:apply-templates select="/ip-route" />
  </xsl:template>
  
  <xsl:template name="iproute-arg">
    <xsl:param name="arg"/>
    <xsl:if test="string-length(translate($arg,'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_:-./','')) != 0">
      <xsl:message terminate="yes">Illegal characters in iproute attributes: <xsl:value-of select="$arg"/></xsl:message>
    </xsl:if>
    <xsl:value-of select="$arg"/>
  </xsl:template>

  <xsl:template name="write-routing-sysctl">
    <xsl:param name="name"/>
    <xsl:param name="value"/>
    
    <xsl:if test="string-length($name) and string-length($value)">
      <xsl:text>echo </xsl:text>
      <xsl:value-of select="$value"/>
      <xsl:text> &gt; </xsl:text>
      <xsl:value-of select="concat('/proc/sys/net/ipv4/route/',$name)"/>
      <xsl:text>&#xA;</xsl:text>
    </xsl:if>
  </xsl:template>
  
  <!-- We iterate the rules directly in order, pulling in tables that are called by rules so we can
       combine the criteria -->
  <xsl:template match="ip-route">
    <xsl:for-each select="@*[name()='error_burst' or name()='gc_elasticity' or name()='gc_min_interval_ms' or
                             name()='max_delay' or name()='min_delay' or name()='redirect_load' or
                             name()='secret_interval' or name()='error_cost' or name()='gc_interval' or
                             name()='gc_thresh' or name()='max_size' or name()='min_pmtu' or
                             name()='redirect_number' or name()='gc_min_interval' or name()='gc_timeout min_adv_mss' or
                             name()='mtu_expires redirect_silence']">
      <xsl:call-template name="write-routing-sysctl">
        <xsl:with-param name="name" select="name()"/>
        <xsl:with-param name="value" select="."/>
      </xsl:call-template>
    </xsl:for-each>
    
    <xsl:apply-templates select="rules/rule[@priority and 
                  not(@priority =     0 and (@table='local'   or @table=255))
              and not(@priority = 32766 and (@table='main'   or @table=254))
              and not(@priority = 32767 and (@table='default' or @table=253)) ]"/>
    <xsl:apply-templates select="tables/table[@id!='local' and @id!='default' and @id!='255' and @id!='254']/route[not((../@id='main' or string-length(../@id)=0) and @scope='link')]"/>

  </xsl:template>

  <xsl:template match="rules/rule">
    <xsl:variable name="rule" select="."/>

    <xsl:text>ip rule add</xsl:text>
    <xsl:if test="@from">
      <xsl:text> from </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@from"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@to">
      <xsl:text> to </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@to"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@tos">
      <xsl:text> tos </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@tos"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@fwmark">
      <xsl:text> fwmark </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@fwmark"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@dev">
      <xsl:text> dev </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@dev"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@priority">
      <xsl:text> pref </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@priority"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@table">
      <xsl:text> table </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@table"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@type">
      <xsl:text> </xsl:text>
      <xsl:choose>
        <xsl:when test="@type='none'">
          <xsl:text>0</xsl:text>
        </xsl:when>
        <xsl:otherwise test="@type='none'">
          <xsl:call-template name="iproute-arg">
            <xsl:with-param name="arg" select="@type"/>
          </xsl:call-template>
        </xsl:otherwise>
      </xsl:choose>        
    </xsl:if>
    <xsl:if test="@realm">
      <xsl:text> realms </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@realm"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>
  
  <xsl:template match="tables/table/route">
    <xsl:text>ip route add</xsl:text>
    <xsl:if test="@type">
      <xsl:text> </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@type"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@prefix">
      <xsl:text> </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@prefix"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@dev">
      <xsl:text> dev </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@dev"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@tos">
      <xsl:text> tos </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@tos"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="../@id and ../@id != 'main' and string-length(../@id)">
      <xsl:text> table </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="../@id"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@proto">
      <xsl:text> proto </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@proto"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@scope">
      <xsl:text> scope </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@scope"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@src">
      <xsl:text> src </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@src"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@metric">
      <xsl:text> metric </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@metric"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@mtu">
      <xsl:text> mtu </xsl:text>
      <xsl:if test="@mtu-lock='yes' or @mtu-lock='on' or @mtu-lock='1'">
        <xsl:text>lock </xsl:text>
      </xsl:if>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@mtu"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@advmss">
      <xsl:text> advmss </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@advmss"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@rtt">
      <xsl:text> rtt </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="substring-before(@rtt,'ms')"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@rttvar">
      <xsl:text> rttvar </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="substring-before(@rttvar,'ms')"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@window">
      <xsl:text> window </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@window"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@cwnd">
      <xsl:text> cwn </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@cwnd"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@realm">
      <xsl:text> realms </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@realm"/>
      </xsl:call-template>
    </xsl:if>
    
    <xsl:apply-templates select="next-hop"/>
          
    <xsl:text>&#xA;</xsl:text>
  </xsl:template>

  <xsl:template match="tables/table/route/next-hop">
    <xsl:text> nexthop</xsl:text>
    <xsl:if test="@via">
      <xsl:text> via </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@via"/>
      </xsl:call-template>
    </xsl:if>
    <!-- don't output dev unless onlink, because interfaces may have changed, so make route look it up -->
    <xsl:if test="@dev and @type">
      <xsl:text> dev </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@dev"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@weight">
      <xsl:text> weight </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@weight"/>
      </xsl:call-template>
    </xsl:if>
    <xsl:if test="@type">
      <xsl:text> </xsl:text>
      <xsl:call-template name="iproute-arg">
        <xsl:with-param name="arg" select="@type"/>
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
  
</xsl:transform>  
ENDOFXSLT
}

main "$@"
_______________________________________________
LARTC mailing list
LARTC@xxxxxxxxxxxxxxx
http://mailman.ds9a.nl/cgi-bin/mailman/listinfo/lartc

[Index of Archives]     [LARTC Home Page]     [Netfilter]     [Netfilter Development]     [Network Development]     [Bugtraq]     [GCC Help]     [Yosemite News]     [Linux Kernel]     [Fedora Users]
  Powered by Linux