Authenticating ospf

This is another of those little tasks that look simple but turn out to be more a little more complex than it first looked.

Authentication in OSPF is performed by sending authentication data in every flow. This can be a password (not very secure) or an MD5 check sum, based on a shared password and sequence number. The receiver checks the data sent is valid, and matches the data it has.

Enabling authentication on Linux

To do any authentication you need to enable it at the area level.

router ospf
  ospf router-id 9.2.3.4
  area 0.0.0.0 authentication

This turns it on for all interfaces – defaulting to password based with a null password. I did this and my connections failed because the two ends of the link were configured differently.

I first had to configure ip ospf authentication null for all interfaces, then enable area authenticate, and the the connections to other systems worked.

interface tap2
   ip ospf area 0.0.0.0
   ip ospf authentication null

interface ...

router ospf
  ospf router-id 9.2.3.4
  area 0.0.0.0 authentication

I could then enable the authentication on an interface by interface basis.

If there is a mismatch,

  • z/OS will report a mismatch,
  • frr quietly drops the packet. I enabled packet trace.

debug ospf packet hello

I got out a trace

OSPF: ... interface enp0s31f6:10.1.0.2: auth-type mismatch, local Null, rcvd Simple
OSPF: ... ospf_read[10.1.0.3]: Header check failed, dropping.

The router ospf … area … authentication is the master switch.

To define authentication on a link, you have to change both ends, then activate the change at the same time at each end.

On z/OS

I could not find how to get OMPROUTE to reread its configuration file after I updated and OSPF entry. There is an option

f OMP1,reconfig

but the documentation says

RECONFIG
Reread the OMPROUTE configuration file. This command ignores all statements in the configuration file except new OSPF_Interface, RIP_Interface, Interface, IPv6_RIP_Interface, and IPv6_Interface
statements.

and I got messages like

EZZ7821I Ignoring duplicate OSPF_Interface statement for 10.1.1.2

For z/OS OMPROUTE to communicate with frr (and CISCO routers) I had to specify the z/OS definition Authentication_… for example

ospf_interface IP_address=10.1.1.2 
      name=ETH1 
      subnet_mask=255.255.255.0 
      Authentication_type=PASSWORD 
      Authentication_Key="colin" 
      ;    

Then stop and restart OMPROUTE.

Using password (or not)

If you use a password, then it flows in clear text. Anyone sniffing your network will see it. It should not be used to protect your system.

On frr

You need router ospf area … authentication. If you have area … authentication message-digest then the password authentication statement on the interface is ignored.

router ospf
  ospf router-id 9.2.3.4
  router-info area
  area 0.0.0.0 authentication

interface tap0
   ip ospf authentication colin
   ...

On z/OS

ospf_interface IP_address=10.1.3.2 
      name=JFPORTCP4 
      subnet_mask=255.255.255.0 
      Authentication_type=PASSWORD 
      Authentication_Key="colin" 
      ; 

Using MD5

Background

An MD5 checksum is calculated from

  • the key – a string of up to 16 bytes
  • key id – an integer in the range 0-255. In the future this key could be used to specify which checksum algorithm to use. Currently only its value is used only as part of the check sum calculation.
  • the increasing sequence number of the flow.

This checksum is calculated and the sequence number and checksum are sent as part of each flow. The remote end performs the same calculation, with the same data, and the checksum value should match.

Because the sequence number changes with every flow, the checksum value changes with every flow. This prevents replay attacks.

The key must be the same on both ends of the connection. Because frr and hardware routers are based in ASCII, an ASCII value must be specified when using z/OS and these routers.

On frr

router ospf
  ospf router-id 9.2.3.4
  area 0.0.0.0 authentication 

interface tap0
   ip ospf authentication message-digest
   ip ospf message-digest-key 3 md5 AAAAAAAAAAAAAAAA

On z/OS

ospf_interface IP_address=10.1.1.2 
      name=ETH1 
      subnet_mask=255.255.255.0 
      Authentication_type=MD5 
      Authentication_Key=0X41414141414141414141414141414141 
      Authentication_Key_ID=3 
      ;
     ;     Authentication_Key=A"AAAAAAAAAAAAAAAA" 

You can either specify the ASCII value A”A…” or as hex “0x4141…” where 0x41 is the value of A in ASCII.

The z/OS documentation is not very clear. My edited version is

Authentication_Key
The value of the authentication key for this interface. This value must be the same for all routers attached to a common medium a link. The coding of this parameter depends on the authentication type being used on this interface.

For authentication type MD5, code the 16-byte authentication key used in the md5 processing for OSPF routers attached to this interface.

This value must be the same at each end.

If the router at the remote end is ASCII based, for example CISCO or Extreme routers, or the frr package on Linux, this value must be specified in ASCII.

You can specify a value in ASCII as A”ABCD…” or as hexadecimal 0x41424344…”, were 41424344 is the ASCII for ABCD.

For non ASCII routers you can specify an ASCII or hexadecimal value.   You can use pwtokey to generate a suitable hexadecimal key from a password.


What does tso netstat neighbour give you?

The command TSO NETSTAT ND gave me

Query Neighbor cache for 2001:db8:1:0:8024:bff:fe45:840c 
  IntfName: IFPORTCP6          IntfType: IPAQENET6 
  LinkLayerAddr: 82240B45840C  State: Reachable 
  Type: Router                 AdvDfltRtr: No 

Query Neighbor cache for fe80::8024:bff:fe45:840c 
  IntfName: IFPORTCP6          IntfType: IPAQENET6 
  LinkLayerAddr: 82240B45840C  State: Reachable 
  Type: Router                 AdvDfltRtr: No 

Query Neighbor cache for fe80::9863:1eff:fe13:1408 
  IntfName: JFPORTCP6          IntfType: IPAQENET6 
  LinkLayerAddr: 9A631E131408  State: Reachable 
  Type: Router                 AdvDfltRtr: No 

On Linux the

ip -6 addr

command gave me

tap1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
    inet6 2001:db8:1:0:b0fd:f92b:8362:577b/64 ...
    inet6 2001:db8:1:0:8024:bff:fe45:840c/64 ...
    inet6 fe80::8024:bff:fe45:840c/64 ...

tap2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
    inet6 fe80::9863:1eff:fe13:1408/64 ...

The TSO output means

  • Query Neighbor cache for 2001:db8:1:0:8024:bff:fe45:840c. The address is one of the addresses on the remote end of the connection. There is an entry because some traffic came via the address.
  • IntfName: IFPORTCP6 The z/OS Interface name used to create the defintion
  • IntfType: IPAQENET6 the OSA-Express QDIO interfaces statement
  • LinkLayerAddr: 82240B45840C
  • State: Reachable Other options can include stale, which means z/OS has not heard anything from this address for a while
  • Type: Router
  • AdvDfltRtr: No. The information passed in the Router Advertisement, said this was connection does not Advertise a Default Router(AdvDfltRtr).

From the NETSTAT ND output we can see data has been received from

  • IFPORTCP6:2001:db8:1:0:8024:bff:fe45:840c
  • IFPORTCP6:fe80::8024:bff:fe45:840c
  • JFPORTCP6:fe80::9863:1eff:fe13:1408

To get data to flow down the 2001…. address I had to use

ping -I 2001:db8:1:0:8024:bff:fe45:840c 2001:db8:1::9

Where the -I says use the interface address.

You can get information about bytes processed by interface (not by address) using the TSO NETSTAT DEVLINKS command.

Getting IP v6 static routing from Linux to/from z/OS

For me this was an epic journey, taking weeks to get working. It was like a magical combination lock, which will not open unless all of the parameters are correct, today has an ‘r’ in the month, and you are standing on one leg. Once you know the secrets, it is easy.

With IP V6 there is a technology called dynamic discovery which is meant to make configuring your IP network much easier. Each node asks the adjacent nodes what IP addresses they have, and so your connection to the next box magically works. I could not get this to work, and thought I would do the simpler task of static configuration – this had similar problems – but they were smaller problems.

There were two three four five six seven key things that were needed to get ping to work in my setup:

The key things

Allow forwarding between interfaces

On Linux

sudo sysctl -w net.ipv6.conf.all.forwarding=1

The documentation says “… conf/all/forwarding – Enable global IPv6 forwarding between all interfaces”.

Clearing the cache

Routing and neighbourhood definitions are cached for a period. If you change a definition, and activate it, an old definition may still be used. I found I got different results if I rebooted, re-ipled, or went for a cup of tea; it worked – then next time I tried it with the same definitions, it did not work. Clearing the routing and neighbourhood cache made it more consistent.

On z/OS use V TCPIP,,PURGECACHE,IFPORTCP6

On Linux use sudo ip -6 neigh flush all

Put a delay between creating definitions and using them.

I had a 2 second delay between creating a definition, and using it, which helped getting it to work. I think data is propagated between the system, and issuing a ping or other command immediately after a definition, was too fast for it,

A timing window

I had scripts to clear and redefine the definitions. Some times if I ran the laptop script then the server script, then ping would not work. If I reran the laptop script, then usually ping worked. Sometimes I had to rerun the server script.

The default route would often change.

The wireless connection to the server was unreliable. There would be a route from my laptop to the server via the wireless. Then a few minutes later the connection to the server would stop, and so alternate routes had to be used, because traffic via the wireless would be dropped.

I got around this problem, by explicit coding of the routes and not needing to use the default definitions. (Also disabled the wireless connection while debugging)

The correct route syntax

I found I was getting “Neigbor Solicitation” instead of the static routing. To prevent this the route on the laptop needed the via…

sudo ip -6 route add 2001:db8:1::9/128 via 2001:db8::2 dev enp0s31f6

and not

sudo ip -6 route add 2001:db8:1::9/128                dev enp0s31f6

See Is “via” needed when creating a Linux IP route?

The z/OS IP address kept changing across IPLs

Why is my z/OS IP address changing when using zPDT, and routing does not work?

Configuration

  • The laptop had an Ethernet connection to the server.
  • The server had an Ethernet like connection to z/OS. This was a tunnel(tap1), looking like an OSA to z/OS

The addresses:

Laptop Ethernet (enp0s31f6)2001:db8:::7
Server Ethernet (eno1)2001:db8:::2
Server Tunnel (tap1)2001:db8:1::3
Z/OS interface (ifacecp6)2001:db1::9

The Laptop side had prefix 2001:db8:0::/64, the z/OS side had prefix 2001:db8:1::/64 . See One minute topic: Understanding IP V6 addressing and routing if these numbers look strange.

Definitions

z/OS routing definitions

BEGINRoutes 
;     Destination      FirstHop          LinkName   Size 
ROUTE default6         2001:db8:99::3    IF2        MTU  1492
ROUTE 2001:db8:99::/64 2001:db8:99::3    IF2        MTU 5000 

ROUTE 2001:db8::/64    2001:db8:1::3     IFPORTCP6  MTU 5000 
ROUTE 2001:db8:1::/64  2001:db8:1::3     IFPORTCP6  MTU 5000 
                                                                              
ENDRoutes 

Where

  • default6 says if no other routes match, then send the traffic down IF2 connection. At the remote end of the IF2 connection, it has IP address 2001:db8:99::3.
  • Traffic for 2001:db8:99::/64 should be sent down interface IF2 – which has an address 2001:db8:99::3 at the remote end
  • Traffic for 2001:db8::/64 (2001:db8:0::/64) should be sent down interface IFPORTCP6 which has address 2001:db8:1::3 at the remote end.
  • Traffic for 2001:db8:1::/64 should be sent down interface IFPORTCP6 which has address 2001:db8:1::3 at the remote end.

I needed a route for both 2001:db8::/64 and 2001:db8:1::/64 as one was the route to the laptop, the other was the route to the Linux server.

Linux Server machine

On my Linux machine I had

from ip -6 addr

tap1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 state UNKNOWN qlen 1000
 inet6 2001:db8:1::3/64 scope global 
    valid_lft forever preferred_lft forever
 inet6 2001:db8::3/64 scope global 
    valid_lft forever preferred_lft forever
 inet6 fe80::e852:31ff:fe0f:81da/64 scope link 
    valid_lft forever preferred_lft forever

I used the global address 2001:db8:1::3 in my z/OS routing statement.

The documentation implies I should use the link-local address fe80::e852:31ff:fe0f:81da in my static z/OS definitions, but I could not see how to use this, as it changed every time I ipled my z/OS. This means I need to explicitly define an address on Linux for this connection ( 2001:db8:1::3).

Linux Server definitions

On my Linux server I defined static definitions.

sudo sysctl -w net.ipv6.conf.all.forwarding=1

# clear the state every time
sudo ip -6 route flush root 2001:db8:1::/64
sudo ip -6 route flush root 2001:db8::/64
sudo ip -6 neigh flush all 

# define the interface to z/OS
sudo ip -6 addr del 2001:db8:1::3/64 dev tap1
sudo ip -6 addr add 2001:db8:1::3/64 dev tap1

sudo ip -6 addr del 2001:db8::2/64 dev eno1
sudo ip -6 addr add 2001:db8::2/64 dev eno1


sudo ip -6 route del 2001:db8::/64 dev eno1
sudo ip -6 route add 2001:db8::/64 dev eno1

sudo ip -6 route del 2001:db8:1::9 dev tap1
sudo ip -6 route add  2001:db8:1::/64   dev tap1

# sudo traceroute -d -m 2 -n -q 1 -I    2001:db8::7 
# ping 2001:db8::7 -c 1 -r
# ping 2001:db8:1::9 -c 1 -r

This script grew as I added all of the options to get it to work.

The statements are

sudo sysctl -w net.ipv6.conf.all.forwarding=1

This enables the cross interface traffic.

sudo ip -6 route flush root 2001:db8:1::/64
sudo ip -6 route flush root 2001:db8::/64
sudo ip -6 neigh flush all

These clear the routing for the two addresses, and for the neighbourhood cache. I do not know if these are required, without them the results were not consistent.

#give the interface to z/OS an explicit address
sudo ip -6 addr del 2001:db8:1::3/64 dev tap1
sudo ip -6 addr add 2001:db8:1::3/64 dev tap1


#give the connection to the Laptop an explicit address
sudo ip -6 addr del 2001:db8::2/64 dev eno1
sudo ip -6 addr add 2001:db8::2/64 dev eno1

These deleted then created global addresses for the server end of the interfaces.

sudo ip -6 route del 2001:db8::/64 dev eno1
sudo ip -6 route add 2001:db8::/64 dev eno1


sudo ip -6 route del 2001:db8:1:: dev tap1
sudo ip -6 route add 2001:db8:1:: dev tap1

These deleted and created routes the traffic to the interfaces. I could have used route rep…

Linux Laptop definitions

#Give the ethernet connection to the server an explicit address
sudo ip -6 addr add 2001:db8::19 dev enp0s31f6

#create the route to the server using the via
sudo ip -6 route del 2001:db8:1::/64 dev enp0s31f6
sudo ip -6 route add 2001:db8:1::/64 via 2001:db8::2 dev enp0s31f6

I needed to specify

  • an explicit to the address of the interface to the server, so it could be used as a destination from z/OS.
  • the route to get to the server. I needed to specify the via, so the static route was used directly. Without the via, it tried to use Neighbourhood discovery.

Pinging

For “ping” to work, the packet has to reach the destination and the reply get back to the originator. See Understanding ping and why it does not answer.

If I pinged 2001:db8:1::9 (z/OS) from the Linux server (the end of the IFPORTCP6 connection) the traffic came from address 2001:db8:1::3, The reply was sent back using the matching 2001:db8:1::/64 definitions.

If I pinged 2001:db8:1::9 (z/OS) from my laptop, through the Linux server to z/OS, the traffic came from address 2001:db8::7. The reply was sent back using the matching 2001:db8::/64 definitions.

If I pinged 2001:db8::7 (laptop) from z/OS it was sent back using the matching 2001:db8::/64 definitions.

Understanding radvd with IPV6 on Linux.

My two day project to deploy IP V6 dynamic routing, turned into an eight week project before I got it to work.

I am documenting a lot of what I learned – today’s experience is understanding what radvd is and what the configuration options mean. I found a lot of documentation – most of which either assumed you know a lot, or only provided an incomplete description.

High level view of what radvd provides

There are several ways of providing configuration to TCPIP. One way is using radvd (on Linux).

In a configuration file you give

  • An interface name
    • Which IP addresses (and address ranges) are available at the remote end of the connection.
    • Which IP addresses (and address ranges) are available at the local end of the connection

From the information about the local end, the remote end can build its routing tables.

Background IP routing

With IP you can have

  • static routing, where you explicitly give the routes to a destination – such as to get to a.b.c.d – go via xyz interface. You have to do a lot of administration defining the addresses of each end of an interface (think Ethernet cable)
  • dynamic routing, and neighbourhood discovery, where the system automatically finds the neighbours and there is less work for an administrator to do.

With IPv6 you have

  • global addresses with IP addresses like 2001:db8:1:16…..
  • link-local addresses. These are specific to an interface. An Ethernet connection can have many terminals hanging off ‘the bus’. The link-local address is only used within the connection. A different Ethernet cable can use the same address. There is no problem as the addresses are only used with the cable.
  • Neighbour Discovery. Rather than specify every thing as you do with static routing, IP V6 supports Neighbour Discovery, where each node can tell connected nodes, what routes and IP addresses it knows about. This is documented in the specification. This supports
    • Router Advertisement (RA), (“Hello, I’m a router, I know about the following addresses and routes”),
    • Router Solicitation (RS), (“Hello, I’ve just started up, are there any routers out there?”),
    • Neighbour Solicitation(NS) (“Does anyone have this IP address?”), and
    • Neighbour Advertisement (Usually in response to a Neighbour Solicitation, “I have this address”).

What is radvd?

The radvd program is a Router Advertisment Daemon (RADvd) which provides fakes router information – but is not a router.

You specify a configuration file. The syntax is defined here.

You can specify that this interface provides a “default” route. See here.

Example definition

I have a Linux Server, and a laptop running Linux connected by an Ethernet cable.

For the server, the radvd.conf file has

# define the ethernet connection
interface eno1
{
AdvSendAdvert on;
MaxRtrAdvInterval 60;
MinDelayBetweenRAs 60;

prefix 2001:ccc::/64
{
# AdvOnLink on;
# AdvAutonomous on;
AdvRouterAddr on;
};

# route 2001:ff99::/64
# {
# # AdvRoutePreference medium;
# # 3600 = 1 hour
# AdvRouteLifetime 3600;
#
# };
};

The key information is

Comments

Data following #

The name of the interface

interface eno1{….};

prefix statement

prefix 2001:ccc::/64{…}

2001:ccc::/64 is the ipv6 address range or, to say it another way, ipv6 addresses with the left 64 bits beginning with 2001:0ccc:0000:0000. In IP V6 this is known as the prefix.

Basically this prefix statement means “this interface is a route to the specified prefix”.

This creates some addresses on the server for the connection.

eno1    inet6 2001:ccc::e02a:943b:3642:1d73/64 scope global temporary dynamic...       
eno1    inet6 2001:ccc::dbf:5c90:61a6:20ae/64 scope global dynamic mngtmpaddr...
eno1    inet6 2001:ccc::c48b:e8f1:495c:5b52/64 scope global temporary dynamic ...       
eno1    inet6 2001:ccc::2d8:61ff:fee9:312a/64 scope global dynamic mngtmpaddr ...

create routes

The prefix statement creates a route on the server to get to the laptop

2001:ccc::/64 dev eno1 proto ra metric 100 pref medium
2001:ccc::/64 dev eno1 proto kernel metric 256 ...

This says that on the server, if there is a request for an address in the range 2001:ccc::/64 send it via device eno1.

route 2001:ff99::/64 statement.

If present this passes information to the remote end of the connection, see below. It says “I (the server) know how to route to 2001:ff99::/64”.

It the routing address does not show up in any ip -6 commands on the server.

I could see the Route Information data flowing across the network within a Router Advertisment packet. But I could not see it being used at the remote end.

I think the best thing to do is ignore the route statement.

Polling

The radvd code periodically sends information along the connection to the other end, at an interval you specify. See radvdump below on how to display it.

At the other end of the connection…

At the remote (laptop) end of the connection, using WiresShark to display the data received, shows a Router Advertisement with

Internet Protocol Version 6, Src: fe80::a2f0:9936:ddfd:95fa, Dst: ff02::1
Internet Control Message Protocol v6
    Type: Router Advertisement (134)
    ...
    ICMPv6 Option (Prefix information : 2001:ccc::/64)
    ICMPv6 Option (Route Information : Medium 2001:ff99::/64)
    ICMPv6 Option (Source link-layer address : 00:d8:61:e9:31:2a)

Where

  • fe80::a2f0:9936:ddfd:95fa is the link-local address on the server machine
  • ff02::1 the multicast address “All nodes” on a link (link-local scope)”
  • Prefix information : 2001:ccc::7/64 the prefix of the IP address 2001:ccc:0:0…
  • Route Information : Medium 2001:ff99::/64 This is from the “route” in the radvd configuration file on the Linux Server. It tells the laptop that this connection knows about routing to 2001:ccc::/64 .

From the route information it dynamically creates a route on the laptop to the server.

2001:ff99::/64 via fe80::a2f0:9936:ddfd:95fa dev enp0s31f6 proto ra metric 100 ...

This creates a route to 2001:ff99::/64 via the IP address fe80::….95fa, from the laptop end of the Ethernet connection with name enp0s31f6 to where-ever the connection goes to (in this example it goes to my server).

If I ping 2001:ffcc::9 from the laptop, it will use this route on its way to the z/OS server.

Connection to z/OS

Within the radvd.conf file is the definition to get to z/OS. This interface looks like an Ethernet (the ip -6 link command gives link/ether). This is for a different radvd configuration file to the earlier example.

interface  tap1
{
   AdvSendAdvert on; 
   MaxRtrAdvInterval 60;
   MinDelayBetweenRAs  60; 
   AdvManagedFlag  on;
   AdvOtherConfigFlag on;
   
   prefix 2001:db8:1::/64
   {
     AdvOnLink on;
     AdvAutonomous on;
     AdvRouterAddr on;
    
   };
   prefix 2001:db8:1::99/128
   {
     AdvOnLink on;
     AdvAutonomous on;
     AdvRouterAddr on;
    
   };

   route 2001:db8::/64
   {
     AdvRoutePreference medium;
     AdvRouteLifetime 3100;
   
   };
};

This says there are IP addresses in the range 2001:db8:1::/64 via this connection. z/OS reads the Router Advertisement and creates a route for them. TSO Netstat route gives

DestIP:   2001:db8:1::/64 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UD                MTU:     9000 

The explicit IP address, with the 128 to specify use the whole value, rather than just the prefix,

 prefix 2001:db8:1::99/128
   {
     AdvOnLink on;
     AdvAutonomous on;
     AdvRouterAddr on;    
   };

creates a route in z/OS; from TSO NETSTAT ROUTE

DestIP:   2001:db8:1::99/128 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UHD               MTU:     9000 

In UHD, the U says the interface is Up, the H says this is for a host (a specific end point), and the D says this is dynamically created.

If you try to ping 2001:db8:1::99 from the server, a Neighbour Solicitation request is sent from the server to z/OS, asking “do you have 2001:db8:1:99?”. My z/OS did not have that defined and so did not respond.

When I defined this address on z/OS TCPIP using the obeyfile

INTERFACE IFPORTCP6  DELETE 
INTERFACE IFPORTCP6 
    DEFINE IPAQENET6 
    CHPIDTYPE OSD 
    PORTNAME PORTCP 
    INTFID 7:7:7:7 
                                             
INTERFACE IFPORTCP6 
    ADDADDR 2001:DB8:1::9 
INTERFACE IFPORTCP6 
    ADDADDR 2001:DB8:9::9 
INTERFACE IFPORTCP6 
    ADDADDR 2001:DB8:1::99 

(And used

  • v tcpip,,sto,ifportcp6
  • v tcpip,,obeyfile,USER.Z24C.TCPPARMS(IFACE61)
  • v tcpip,,sta,ifportcp6

to activate it)

After this, the ping was successful because there was a neighbour solicitation for 2001:db8:1::99, and z/OS replied with Neighbour Advertisement of 2001:db8:1::9 saying I have it.

Create a default route

If you specify AdvDefaultLifetime 0 on the interface, this indicates that the router is not a default router and should not appear on the default router list in the Router Advertiser broadcasts. If the value is non zero, the recipient, can use this connection as a default route, for example there is no existing default route. A statically defined default will be used in preference to a dynamically defined one.

Use radvdump to display what it sent in the RA

You can use sudo radvdump to display what is being sent in the Router Advertiser message broadcast over multicast. It looks just like the radvd.conf file, but with all of the values filled in, so you can see the defaults.

New (well, old) job name symbol simplifies JCL!

This function has been around since z/OS 2.3, so is not that new!

There are JCL symbols

&SYSJOBNM

You can use it in started tasks and Jobs

S OMPROUTE.OMP4 JCL symbol &SYSJOBNM gives OMROUTE

S OMPROUTE,JOBNAME=OMP4  JCL symbol &SYSJOBNM gives OMP4

You can use this like

//PARM1 DD DISP=SHR,DSN=USER.PARMLIB(X&SYSJOBNM)
//PARM2 DD DISP=SHR,DSN=USER.PARMLIB(Y&SYSJOBNM)  

and so have data sets based on the invoked job name.  This means you can simplify your started task procedures, and may not need the user to specify overrides on the start command.  

Note: If you move to using JOBNAME= instead of specifying a .identifier, you may need to change other definitions. For example TCPIP defines port 520 with jobname OMPROUTE. If you switch to using OMPROUTE,JOBNAME=OMP1 this jobname will need to change to OMP1.

&SYSJOBID

gives a value like STC08460 for a started task.

&SYSUID

gives the userid like IBMUSER

&JOBNAME

gives the job assigned to the address space in which the JCL is converted, so JES2 for me.

Is “via” needed when creating a Linux IP route?

To get static routing working I needed a route like one of

# specific destination
sudo ip -6 route add fc:1::9/128 via fc::2 dev enp0s31f6r
sudo ip -6 route add fc:1::9/128  via fc::2 
#range of addresses
sudo ip -6 route add fc:1::/64 via fc::2  dev enp0s31f6
sudo ip -6 route add fc:1::/64 via fc::2 

If I a route without the via

sudo ip -6 route add fc:1::9/128 dev enp0s31f6

then it ignored my static routing and did Neighbor Solicitation; it asked adjacent systems if they had knew about the IP address fc:1::9. This is an IP V6 Neighbour Discovery facility.

There were hints around the internet that if the next hop address is not specified, then the “next hop router” will try to locate the passed address.

So the short answer to the question is: “yes. You should specify it when using static routing”.

Understanding IP V6 NETSTAT ROUTE on z/OS information

I struggled with the output of the TSO NETSTAT ROUTE command.

Below is an example from my system. The IBM documentation is here

IPv6 Destinations 
DestIP:   Default 
  Gw:     2001:db8:1::3 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UGS               MTU:     1492 
DestIP:   ::1/128 
  Gw:     :: 
  Intf:   LOOPBACK6         Refcnt:  0000000000 
  Flgs:   UH                MTU:     65535 
DestIP:   2001:db8::/64 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   US                MTU:     5000 
DestIP:   2001:db8:1::/64 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UD                MTU:     9000 
DestIP:   2001:db8:1::3/128 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UHS               MTU:     5000 

DestIP: Default this is statically set up with default6

Gw: 2001:db8:1::3 This is (one of) the IP address at the remote end of the connection.

Intf: IFPORTCP6 The z/OS interface name is IFPORTCP6

The flags are

U – The route is up.

G – The route uses a gateway.

H – The route is to a host rather than to a network.

S – The route is a static route not replaceable by a routing daemon or router advertisements (IPv6).

D – The route was created dynamically by ICMP processing or router advertisements (IPv6) (possibly OMPROUTE).

DestIP: 2001:db8::/64 This is for IP addresses 2001:0DB8:0000:0000:something there are 64 bits in the significant part of the address. This applies to 2001:0DB8:0:0:0:0:0:99 and 2001:0DB8:0:0:FFFF:0:0:99 for example

DestIP: 2001:db8:1::3/128 This says all 128 bits are significant in the address. This is the 2001:db8:0:0:0:0:0:3 and no other address.

RefCnt Reference count – the current number of active users for the route. See below.

Where does an entry come from?

  • An entry can be statically configured between BEGINRoutes… ENDoutes.
  • An entry can be dynamically configured from an adjacent system. For example
    • a prefix entry when using radvd – this defines IP address ranges into or through the z/OS host
    • a route entry when using radvd, this defines IP address ranges going off the host, to the other end of the connection.
  • An entry be generated dynamically from OSPF and RIP. On z/OS these are usually configured with the OMPROUTE address space. See below.

A statically defined entry has an S in the Flgs

A dynamic entry has a D in the Flgs -sometimes – see below.

Why does Gw: sometimes have a value?

Gw: has a value when

  • it was specified in the static definitions
  • the DestIP entry was created dynamically, for example as a route …{} statement in radvd. This is an output entry, so the Gw: is part of the definition.

Note: a radvd prefix… {} entry is inbound, so the gateway is irrelevant.

I see this as it is only relevant for connections out of z/OS. When traffic comes into the host, you do not care which gateway it came from.

What does refcnt mean for a DestIP?

The documentation it says “Reference count (RefCnt): The current number of active users for the route.”

When I pinged z/OS ten times from 2001:db8::7, the RefCnt for DestIP: 2001:db8::/80 increased by 10.

When I pinged z/OS ten times from another address, the RefCnt values were unchanged.

Issuing a traceroute to the system did not increment any values.

I could find no active connections to this interface, so all in all this field is bit of a mystery.

The Linux documentation says The reference count (i.e. attached processes via this socket), so the z/OS meaning may be a partial historical count of usage rather than the number of active users.

What is the default value?

This was a surprise. I had defined a static route using default6, and this was in the netstat route display output.

When I used

tso netstat route radv

to display the routes added via Router Advertisement it gave me a list including a Default.

IPv6 Destinations 
DestIP:   Default 
  Gw:     fe80::dce0:8fff:fe42:127f 
  Intf:   IFPORTCP6         MTU:  0 
DestIP:   2001:db8::/80 
  Gw:     fe80::dce0:8fff:fe42:127f 
  Intf:   IFPORTCP6         MTU:  0 
DestIP:   2001:db8:0:0:1::/80 
  Gw:     :: 
  Intf:   IFPORTCP6         MTU:  0 
DestIP:   2001:db9::/32 
  Gw:     fe80::dce0:8fff:fe42:127f 
  Intf:   IFPORTCP6         MTU:  0 
DestIP:   2002:db8::/64 
  Gw:     fe80::dce0:8fff:fe42:127f 
  Intf:   IFPORTCP6         MTU:  0 

If the Router Advertisment data has AdvDefaultLifetime > 0 for the interface then a “Default” is generated, else no default is generated.

The wireshark trace has

Internet Control Message Protocol v6
    Type: Router Advertisement (134)
    ...
    Cur hop limit: 64
    Flags: 0xc0, Managed address configuration, ...
    Router lifetime (s): 0 

The MTU value is what was passed in via the RA data. Change this value in the radvd configuration, and the z/OS value changes.

When I removed my statically defined default6, this default became active with

DestIP:   Default 
  Gw:     fe80::dce0:8fff:fe42:127f 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UGD               MTU:     9000 

Note: It seems you can have only one active Default, even with IPCONIG6 MULIPATH option. I do not know which default becomes active if you have more than one dynamically defined

The detail option

If you use TSO NETSTAT ROUTE DETAIL you get additional information.

Metric: 00000001 
MVS Specific Configured Parameters: 
  MaxReTransmitTime:  120.000   MinReTransmitTime: 0.500 
  RoundTripGain:      0.125     VarianceGain:      0.250 
  VarianceMultiplier: 2.000     DelayAcks:         Yes d

These numbers look like defaults, and I got them even when not traffic had flowed over the connection.

OMPROUTE

OMPROUTE can

  • Provides some “dynamic” information about default IP6 routes
  • It listens to messages from other routers, and can update the routing tables

Sometimes

Without OMPROUTE, routes that were dynamically created, for example using radvd on Linx, which broadcast z/OS address ranges to z/OS, and advertised “come to me for these address ranges”.

These could be seen as Dynamic, for example the D in UD below.

DestIP:   2001:db8:1::/64 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UD                MTU:     9000 

If you start OMPROUTE, the “Dynamic address” now come out as “C”

DestIP:   2001:db8:1::/64 
  Gw:     :: 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UC                MTU:     9000 

I’m bored with giving the same reply to messages on the console.

When using z/OS I have to reply to messages, for example at startup and shutdown. After several months of this I was getting bored, and found z/OS has an auto reply capability.

In the SYS1.PARMLIB concatenation you can have AUTORxx members.

For example in SYS1.PARMLIB(AUTOR00) is

/* ARC0380A RECALL WAITING FOR VOLUME volser IN USE BY HOST procid, */ 
/*          FUNCTION function. REPLY WAIT, CANCEL, OR MOUNT         */ 
/*                                                                  */ 
/* Rule: 1                                                          */ 
/*                                                                  */ 
   Msgid(ARC0380A)   Delay(60S) Reply(CANCEL)                          

So if you get message ARC0380A, after 60 seconds it will reply CANCEL. If you are quick you could reply with something else.

If you always reply with the same value you could specify DELAY(0S)… but this means you cannot reply to the message with a different value… DELAY(5S) may be better.

You can specify multiple values in the REPLY(a,b,c), and can use system symbolics such as &SYSNAME.

REPLY(‘system=&SYSNAME.’,‘,option1,option2’)

My parmlib member IEASYS00 includes AUTOR=(00,DT), so members AUTOR00 and AUTORDT are used.

Getting OMPROUTE to work on ADCD

While working on getting IP V6 to work, I had problems getting my routing on z/OS working properly. I spent a few days working with TCPIP dynamic routing. What I have written below is what I have learned, it may well be wrong!

The problem…

With TCPIP you can define static routes. For example

  BEGINRoutes 
  ; Destination        SubnetMask    FirstHop       LinkName    Size 
  ROUTE 9.114.209.0    255.255.255.0    =            ETH2   MTU 1492 
  ROUTE DEFAULT             10.1.1.1                 ETH1   MTU 1492 
  ROUTE DEFAULT6  FE80::8C46:FAFF:FE86:1721    IFPORTCP6  MTU 1492 
  ENDRoutes 

This says

  • To send data IP V4 address9.114.209.* go via the link ETH2.
  • Otherwise send all IPV4 traffic to link ETH1 which has address 10.1.1.
  • Send all IP6 traffic to link IFPORTCP6 – which has an IP address of FE80::8C46:FAFF:FE86:1721.

Maintaining lists like these quickly become unmanageable as the size of the network increases.

Dynamically finding the configuration

To solve this problem TCPIP was enhanced to allow the system to discover information. The OMPROUTE daemon manages this.

When OMPROUTE is started, it queries the TCPIP configuration. It merges this information with its configuration data, and sends a request out to participating interfaces “Hello – who are you connected to”. From this information it can build a pictures of the network.

The “Hello – who are you connected to” could produce a lot of output; you can get information about all the links and routers in your environment.

You can divide your network (from an OMPROUTE perspective ) into areas, and so only routers within an area exchange information. The areas have a number like 1.1.1.1.

The main backbone has number 0.0.0.0 You can send requests between areas. Routers which connect an area to the backbone are called Area Border Routers.

An Area Border Router can be configured as a default route – if a router does not know where to send the packet, send it to the Area Border Router.

Where is this information stored?

It looks like this configuration information is stored in the TCPIP address space, because if you shut down OMPROUTE, the configuration persists. I do not think the information will be updated, until OMPROUTE is restarted.

Routing packets

When a packet needs to be sent to a destination it uses the information provided by OMPROUTE. It uses two algorithms,

  • OSPF -Open shortest path first. As the state of an element in the network changes – the change is broadcast to all the routers. This means a router can quickly respond to a configuration change.
  • RIP – Routing interface protocol. This uses the number of hops. (I would have thought this was the same as the shortest path.) A RIP router has a 30 second heartbeat with its connected networks. If it has not received a response after 180 seconds, it assumes the network link is down.

It looks like OSPF is the better protocol, it is faster to respond to changes, and listens to changes, rather than using a heartbeat, and looks like it scales better.

Creating OMPROUTE

The documentation is not the clearest. I think it assumes you are an expert before you start.

The IBM documentation is here.

My additional steps included

  • The …TCPIP(PROF2) had “520 UDP OROUTED ; RouteD Server”. This name needs to match your started task proc. Either change the TCPIP profile, or use OROUTED as your proc name.
  • OMPROUTE procedure needs a userid with an OMVS segment.
  • Create RACF started task profile. Profile OMPROUTE.* with CLASS(STARTED). STDATA INFORMATION user=…
  • I created USER.Z25A.tcpparms(omp) and copied TCPIP.SEZAINST(EZAORCFG) into it.
  • Initially I forgot to use define the RACF profile CLASS(OPERCMDS) MVS.ROUTEMGR.OMPROUTE

My OMPROUTE procedure was

//OMPROUTE PROC 
//OMPROUTE EXEC PGM=OMPROUTE,REGION=0M,TIME=NOLIMIT, 
// PARM=('ENVAR("_CEE_ENVFILE_S=DD:STDENV")/') 
//* 
//OMPCFG DD DISP=SHR,DSN=USER.Z25A.TCPPARMS(OMP) 
//STDENV DD * 
RESOLVER_CONFIG=//'ADCD.Z25A.TCPPARMS(TCPDATA)' 
OMPROUTE_DEBUG_FILE=/tmp/logs/omproute.debug 
OMPROUTE_IPV6_DEBUG_FILE=/tmp/logs/omprout6.debug 
OMPROUTE_DEBUG_FILE_CONTROL=1000,5 
//SYSPRINT DD SYSOUT=* 
//SYSOUT   DD SYSOUT=* 
//CEEDUMP  DD SYSOUT=*,DCB=(RECFM=FB,LRECL=132,BLKSIZE=132) 
//  PEND 

The OMPROUTE configuration is in //OMPCFG … USER.Z25A.TCPPARMS(OMP)

Setting up syslogd

If you are not running SYSLOGD, all of the console messages are prefixed with

BPXF024I (TCPIP) Oct 6 10:11:10 omproute 67174435 : …

When running with SYSLOGD, the default syslogd configuration is to write errors to a file (for example into /var/log/2022/10/06/errors) , and throws away the remainder. I added the following to my syslogd configuration and restarted syslogd.

*.OMPROUTE..* /var/log/%Y/%m/%d/omproute

The messages were then put into the file, and were like

Oct 6 11:52:19 S0W1 omproute[67174443]: EZZ7800I OMPROUTE starting
Oct 6 11:52:24 S0W1 omproute[67174443]: EZZ7817I Using defined OSPF protocol 89
Oct 6 11:52:24 S0W1 omproute[67174443]: EZZ7817I Using defined OSPF protocol 89
Oct 6 11:52:24 S0W1 omproute[67174443]: EZZ7838I Using configuration file: dd:OMPCFG=USER.Z25A.TCPPARMS(OMP)

After OMPROUTE started, In this file I had 19 messages. There were 6 message on the job log.

Configuring OMPROUTE

I had a TCPIP configuration with

INTERFACE IFPORTCP6 
  DEFINE IPAQENET6 
  CHPIDTYPE OSD 
  PORTNAME PORTCP 
  INTFID 7:7:7:7 
  IPADDR FE00::66:7:7:7 

DEVICE PORTB MPCIPA 
 LINK ETH2 IPAQENET PORTB 
HOME 172.26.1.9      ETH2 
                                                 
DEVICE PORTA MPCIPA 
 LINK ETH1 IPAQENET PORTA 
HOME 10.1.1.2        ETH1 
                                                 

In my configuration file I had

OSPF_INTERFACE 
       Attaches_to_Area =0.0.0.0 
       IP_Address=10.1.1.2 
       Subnet_mask=255.255.255.0 
       NAME=ETH1 
       ; 
RIP_INTERFACE 
       IP_Address=10.1.1.2 
       Subnet_mask=255.255.255.0 
       NAME=PORTA 
       ; 
IPv6_OSPF 
       RouterID = 7.7.7.7
       ; 
IPV6_OSPF_INTERFACE 
       Attaches_to_Area =0.0.0.0 
       NAME=IFPORTCP6 
       ; 
IPv6_RIP_Interface 
       Name = IFPORTCP6
       ; 

At startup I got messages in the job log

EZZ7871I NO MATCHING INTERFACE STATEMENTS FOR 172.26.1.20 (EZASAMEMVS)                        
EZZ8171I OMPROUTE IPV4 OSPF IS USING ASSIGNED ROUTER ID 10.1.1.2 FROM ETH1 INTERFACE          
EZZ8171I OMPROUTE IPV6 OSPF IS USING CONFIGURED ROUTER ID 7.7.7.7 FROM IPV6_OSPF STATEMENT     

To resolve the problem with EZASAMEMVS I added

 Interface 
      IP_Address=172.26.1.20 
      Name=EZASAMEMVS 
      subnet_mask=255.255.255.0 
      ; 

Which basically says this interface is not involved in any dynamic routing.

Once this was resolved the messages in the syslogd file were

EZZ7800I OMPROUTE starting
EZZ7817I Using defined OSPF protocol 89
EZZ7817I Using defined OSPF protocol 89
EZZ7838I Using configuration file: dd:OMPCFG=USER.Z25A.TCPPARMS(OMP)
EZZ7883I Processing interface from stack, address 10.1.1.2, name ETH1, index 5, flags 441
EZZ7883I Processing interface from stack, address 172.26.1.20, name EZASAMEMVS, index 2, flags ffff8c50
EZZ7871I No matching interface statements for 172.26.1.20 (EZASAMEMVS)
EZZ7977I Processing IPv6 interface from stack, address fe00::66:7:7:7, name IFPORTCP6, index 51, flags 1, flags2 0
EZZ7977I Processing IPv6 interface from stack, address fe80::7:7:7:7, name IFPORTCP6, index 51, flags 1, flags2 2
EZZ7882I Processing static route from stack, destination 9.114.209.0, Mask 255.255.255.0, gateway 0.0.0.0 , table EZBMAIN
EZZ7882I Processing static route from stack, destination 0.0.0.0, Mask 0.0.0.0, gateway 10.1.1.1 , table EZBMAIN
EZZ7882I Processing static route from stack, destination ::, prefixlen 0, gateway fe80::8c46:faff:fe86:1721 , table EZBMAIN
EZZ8023I The RIP routing protocol is Enabled
EZZ8036I The IPv6 RIP routing protocol is Enabled
EZZ8171I OMPROUTE IPv4 OSPF is using assigned router ID 10.1.1.2 from ETH1 interface
EZZ7937I The IPv4 OSPF routing protocol is Enabled
EZZ8171I OMPROUTE IPv6 OSPF is using configured router ID 7.7.7.7 from IPV6_OSPF statement
EZZ7937I The IPv6 OSPF routing protocol is Enabled
EZZ7898I OMPROUTE Initialization Complete

Displaying information from OMPROUTE

On the console

You can use commands like

d tcpip,,omp,ospf,list,all
d tcpip,,omp,ipv6ospf,all
d tcpip,,omprtoute,rttable
f omproute,ospf,list,all
f omproute,rttable
f omproute,rt6table
f omproute,IPV6OSPF,if
f omproute,IPV6OSPF,list,all
f omproute,IPV6OSPF,statistics
f omproute,IPV6rip,list,all
f omproute,ospf,nbrs

All of which will list all information about the OSPF configuration.

There are three types of resource you an display

  • OSPF – d tcpip,,omp,ospf,list,all or d tcpip,,omp,IPV6ospf,list,all
  • RIP – d tcpip,,omp,rip,list,all or d tcpip,,omp,IPV6rip,list,all
  • Other interface – d tcpip,,omp,generic,all or d tcpip,,omp,generic6,all

From netstat

Once OMPROUTE was active NETSTAT ROUTE gave me

IPv4 Destinations
Destination        Gateway         Flags    Refcnt     Interface
-----------        -------         -----    ------     ---------
Default            10.1.1.1        UGS      0000000000 ETH1
9.114.209.0/24     0.0.0.0         US       0000000000 ETH1
10.1.1.0/24        0.0.0.0         UO       0000000000 ETH1
10.1.1.2/32        0.0.0.0         UH       0000000000 ETH1
127.0.0.1/32       0.0.0.0         UH       0000000002 LOOPBACK
172.26.1.20/32     0.0.0.0         H        0000000000 EZAZCX
172.26.1.20/32     0.0.0.0         H        0000000000 EZASAMEMVS
IPv6 Destinations
DestIP:   Default
  Gw:     fe80::8c46:faff:fe86:1721
  Intf:   IFPORTCP6         Refcnt:  0000000000
  Flgs:   UGS               MTU:     1492
DestIP:   ::1/128
  Gw:     ::
  Intf:   LOOPBACK6         Refcnt:  0000000000
  Flgs:   UH                MTU:     65535
DestIP:   fe00::66:7:7:7/128
  Gw:     ::
  Intf:   IFPORTCP6         Refcnt:  0000000000
  Flgs:   UH                MTU:     9000
DestIP:   fe80::7:7:7:7/128
  Gw:     ::
  Intf:   IFPORTCP6         Refcnt:  0000000000
  Flgs:   UH                MTU:     9000

Where key fields are

  • Flgs UO. U=The interface is UP, and The route was created by OSPF (includes OSPF external routes).

TSO NETSTAT STACK – doesn’t. Get fullscreen output instead.

I was getting annoyed because the TCPIP NETSTAT command displays the output in line mode, and you cannot scroll back to look at it.

The documentation says you can use NETSTAT … STACK in a Rexx program, and use queued() to see how many lines were on the stack. This did not work for me. I always got 0 lines on the stack, until I exited ISPF when they all tried to execute and produced “IKJ56621I INVALID COMMAND NAME SYNTAX”.

A better (and more efficient way – you have to write less code) is create an Rexx program. I used USER.Z24C.CLIST(NS)

/* REXX */ 
ADDRESS ISPEXEC
'ISREDIT MACRO (a)' 
x = outtrap("var.") 
if a   = "" then a   = "HOME" 
address TSO "netstat "a 
x = outtrap("OFF") 
if var.0  = 0 then exit 

"ISREDIT AUTOSAVE OFF PROMPT" 
do y =1 to var.0 
   fn= var.y 
   "ISREDIT LINE_AFTER  .ZLAST     =  (fn)" 
end 

If you use this command within an ISPF edit session, it will append the output to the bottom of the file.

Note: You pass parameters via ISREDIT MACRO(parms) rather than pars args parms.

You can always use DELETE ALL NX to clear the file before running the exec.

It uses AUTOSAVE OFF PROMPT, to make you explicitly save the output – or cancel from the session, and do not accidentally save it.

Extend it

As the output is in an ISPF Editor session, you can write edit macros to process the content, and filter or reformat it.

Shorthand

I extended it so I say “ns h” and it issues the netstat home command etc.

For example

'ISREDIT MACRO (a)' 
if a   = "" then a  = "HOME" 
else 
if a = "i" then a = "devlinks (intfname ifportcp6" 
else 
if a = "h" then a = "home" 
else 
if a = "r" then a = "route addrtype  IPV6" 
else 
if a = "n" then a = "nd" 
address TSO "netstat "a