How do I trace TCP/IP sockets on z/OS?

I stumbled on this by accident.

In the TCPIP.DATA configuration file you can specify

 TRACE SOCKET

I copied the configuration file, and made the change. I used it in my JCL

//SYSTCPD DD DISP=SHR,DSN=USER.Z24C.TCPPARMS(MYDATA)

Note if you use TRACE SOCKET in the configuration file used by every one – then every one will get their sockets traced – which may not be what you want.

The output to SYSPRINT is like

request = HCreate                                                                         
                                                                                          
EZY3829I  pre   0xe3e2d9c2 00c00001 00010000 00000020 e3c3d7c9 d7404040 00000000 00000000 
EZY3830I        0x00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
EZY3831I        0x00000000 1fa77318 00000000 00000000 00000000 00000080 00000000 00000000 
EZY3832I        0x00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
EZY3833I        0xffff0002 00000000 00000000 40404040 40404040 f18681f7 f68686f8 00000000 
EZY3834I        0x00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
                                                                                          
request = HCreate                                                                         
                                                                                          
EZY3835I  post  0xe3e2d9c2 00c00001 00010000 00000020 e3c3d7c9 d7404040 00000000 00000000 
EZY3830I        0x7f5ec0f0 00010000 00000000 00000000 00000000 00000000 00000000 00000000 
EZY3831I        0x00000000 1fa77318 00000000 00000000 00000000 00000080 00000000 00000000 
EZY3832I        0x00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
EZY3833I        0xffff0002 00000031 00000000 40404040 40404040 f18681f7 f68686f8 00000000 
EZY3834I        0x00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 

These messages and the content are not documented – they are for IBM Software Support.

Compiling the TCP/IP samples on z/OS

Communications server (TCPIP) on z/OS provides some samples. I had problems getting these to compile, because the JCL in the documentation was a) wrong and b) about 20 years behind times.

Samples

There are some samples in TCPIP.SEZAINST

  • TCPS: a server which listens on a port
  • TCPC: a client which connects to a server using IP address and port
  • UDPC: C socket UDP client
  • UDPS: C socket UDP server
  • MTCCLNT: C socket Multitasking client
  • MTCSRVR: C socket Multitasking server
  • MTCCSUB: C socket subtask MTCCSUB

The JCL I used is

//COLCOMPI   JOB 1,MSGCLASS=H,COND=(4,LE) 
//S1          JCLLIB ORDER=CBC.SCCNPRC 
// SET LOADLIB=COLIN.LOAD 
// SET LIBPRFX=CEE 
// SET SOURCE=COLIN.C.SOURCE(TCPSORIG) 
//COMPILE  EXEC PROC=EDCCB, 
//       LIBPRFX=&LIBPRFX, 
//       CPARM='OPTFILE(DD:SYSOPTF),LSEARCH(/usr/include/)', 
// BPARM='SIZE=(900K,124K),RENT,LIST,RMODE=ANY,AMODE=31' 
//COMPILE.SYSLIB DD 
//               DD 
//               DD DISP=SHR,DSN=TCPIP.SEZACMAC 
//*              DD DISP=SHR,DSN=TCPIP.SEZANMAC  for IOCTL 
//COMPILE.SYSOPTF DD * 
DEF(_OE_SOCKETS) 
DEF(MVS) 
LIST,SOURCE 
TEST 
RENT ILP32        LO 
INFO(PAR,USE) 
NOMARGINS EXPMAC   SHOWINC XREF 
LANGLVL(EXTENDED) sscom dll 
DEBUG 
/* 
//COMPILE.SYSIN    DD  DISP=SHR,DSN=&SOURCE 
//BIND.SYSLMOD DD DISP=SHR,DSN=&LOADLIB. 
//BIND.SYSLIB  DD DISP=SHR,DSN=TCPIP.SEZARNT1 
//             DD DISP=SHR,DSN=&LIBPRFX..SCEELKED 
//* BIND.GSK     DD DISP=SHR,DSN=SYS1.SIEALNKE 
//* BIND.CSS    DD DISP=SHR,DSN=SYS1.CSSLIB 
//BIND.SYSIN DD * 
  NAME  TCPS(R) 
//START1   EXEC PGM=TCPS,REGION=0M, 
// PARM='4000          ' 
//STEPLIB  DD DISP=SHR,DSN=&LOADLIB 
//SYSERR   DD SYSOUT=*,DCB=(LRECL=200) 
//SYSOUT   DD SYSOUT=*,DCB=(LRECL=200) 
//SYSPRINT DD SYSOUT=*,DCB=(LRECL=200) 

Change the source

The samples do not compile with the above JCL. I needed to remove some includes

#include <manifest.h> 
// #include <bsdtypes.h> 
#include <socket.h> 
#include <in.h> 
// #include <netdb.h> 
#include <stdio.h> 

With the original sample I got compiler messages

ERROR CCN3334 CEE.SCEEH.SYS.H(TYPES):66 Identifier dev_t has already been defined on line 98 of “TCPIP.SEZACMAC(BSDTYPES)”.
ERROR CCN3334 CEE.SCEEH.SYS.H(TYPES):77 Identifier gid_t has already been defined on line 101 of “TCPIP.SEZACMAC(BSDTYPES)”.
ERROR CCN3334 CEE.SCEEH.SYS.H(TYPES):162 Identifier uid_t has already been defined on line 100 of “TCPIP.SEZACMAC(BSDTYPES)”.
ERROR CCN3334 CEE.SCEEH.H(NETDB):87 Identifier in_addr has already been defined on line 158 of “TCPIP.SEZACMAC(IN)”.


INFORMATIONAL CCN3409 TCPIP.SEZAINST(TCPS):133 The static variable “ibmcopyr” is defined but never referenced.

I tried many combinations of #define but could not get it to compile, unless I removed the #includes.

Compile problems I stumbled upon

Identifier dev_t has already been defined on line ...                                                     
Identifier gid_t has already been defined on line ...                                                     
Identifier uid_t has already been defined on line ....

This was caused by the wrong libraries in SYSLIB. I needed

  • CEE.SCEEH.H
  • CEE.SCEEH.SYS.H
  • TCPIP.SEZACMAC
  • TCPIP.SEZANMAC

The compile problems were caused by CEE.SCEEH.SYS.H being missing.

Execution problems

I had some strange execution problem when I tried to use AT-TLS within the program.

EDC5000I No error occurred. (errno2=0x05620062)

The errno2 reason from TSO BPXMTEXT 05620062 was

BPXFSOPN 04/27/18
JRNoFileNoCreatFlag: A service tried to open a nonexistent file without O_CREAT

Action: The open service request cannot be processed. Correct the name or the open flags and retry the operation.

Which seems very strange. I have a feeling that this field is not properly initialised and that this value can be ignored.

How to use display computer hardware in a Libre Office presentation

I was writing a presentation which needed a mixture of computing hardware. There is a Libre Office extension called VRT Network equipment which you can install and have a gallery of hardware, from scanners, printers, firewalls, routers to laptops and tablets and mobile phones.

See VRT Network Equipment Gallery. There are some good instructions here on how to install and use the icons.

Sending an email from z/OS

This started off as a question to me about TLS certificates and ended up with a Python solution.

The short version of this blog post is I could send an email to Gmail using Python – but could not use the IBM CSSMTP solution because it does not support authentication. I was also sent a link to Sending Email from z/OS using Java.

As far as I can tell, SMTP authentication does not use client certificates. If you want to use userid and password, make sure the session is encrypted using TLS.

What types of email are there?

Through JES spool

On my system with id S0W1, I can use TSO XMIT S0W1.IBMUSER and send an email.

The recipient (IBMUSER) can use TSO receive to read the email.

You can use XMIT OVERTHER.MYID to send the email over NJE to other system, such as z/OS, z/VM and z/VSE. You say (on z/OS) xmit node.userid da(‘…’). or xmit node.userid . The JES2 configuration has an IP address and port to send it to a remote spool.

JES2 destinations

You can configure destinations on JES2, and have program (“a writer”) process the node name (destination) data.

You can display the defined destinations using the JES2 command

$DDESTID

Example JCL to write to a destination,

//COPY EXEC PGM=IEBGENER 
//SYSPRINT DD SYSOUT=* 
//SYSIN DD DUMMY 
//SYSUT2 DD SYSOUT=(A,MYDEST),SPIN=UNALLOC 
//SYSUT1 DD * 
line1
/*
// 

This writes the data from SYSUT1 to SYOUT2 which goes to nodename MYDEST.

You configure CSSMP and specify ExtWrtName MYDEST, for CSSMTP to read from.

SMTP

There is Simple Mail Transport Protocol. A stream of data is provided such as

MAIL FROM:<ME@MYSYSTEM.COM> 
RCPT TO:<MYBOSS@MYSYSTEM.COMU> 
RCPT TO:<YOU@yoursystem.com> 
DATA 
Blah blah blah. 
QUIT 

which is sent to the mail server over TCPIP (usually port 25 or 587).

SMTP is a popular protocol. It can support encrypted session encryption. The protocol can support different levels of authorisation.

z/OS provides CSSMTP which reads from the spool and sends the data over TCPIP to a server. However CSSMTP does not seems to support the passing of authentication information to the server – and as most mail servers want you to authenticate before sending emails – CSSMTP cannot be used for these. I know this from the documentation because the AUTH command is not listed in the list of supported SMTP commands, and AUTH does not work!

Using Python to send an email.

I used Python to send an email to my Gmail account. The script I used is

#!/usr/bin/python 
import smtplib 
import ssl 
import sys 
from email.mime.text import MIMEText 
host = "173.194.76.108" 
sender = 'colinpaicetest@gmail.com' 
receivers = ['colinpaicemq@gmail.com','colinpaicetest@gmail.com'] 
msg = MIMEText('This is test mail2') 
msg['Subject'] = 'Test mail3' 
msg['From'] = 'colinpaice3@gmail.com' 
msg['To'] ='colinpaicemq@gmail.com , colinpaicetest@gmail.com' 
with smtplib.SMTP(host , 587) as server: 
      # if you need to validate the certificate sent to client.. you need a context
      context=ssl.create_default_context(
                 purpose=ssl.Purpose.CLIENT_AUTH,cafile="pem.pem") 
      server.starttls(context=context) 
      # server.starttls() # for servers that do not send a certificate 
      print(server.esmtp_features) # print info about the connection such as auth types
      server.login('colinpaice3@gmail.com', 'abcpasswordxyz') 
      server.sendmail(sender, receivers, msg.as_string()) 
      print("Successfully sent email") 
  • context=ssl.create_default_context(purpose=ssl.Purpose.CLIENT_AUTH,cafile=”pem.pem”) is used to set up the CA cert for verifying any certificate sent from the server. GMAIL does not send a certificate, so the CA is not needed, and you can use server.starttls()
  • server.starttls(context=context) creates the TLS session. This is handled by PAGENT.
  • server.login(‘colinpaice3@gmail.com’, ‘ZYC123….’) does the authentication
  • server.sendmail(sender, receivers, msg.as_string()) sends the message.

The id in the sender must match the id in the login.

This does not use PAGENT or AT-TLS.

In the TLS handshake 57 cipher specs were sent to the server, and cipher spec TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xC02B) was used.

Setting up CSSMTP

Basic configuration

Even though I could not use CSSMTP because I could not authenticate, I learned a lot on my doomed journey.

In the CSSMTP configuration I had

ExtWrtName    MYDEST             # the external writer name 
TargetServer 
{ 
   TargetIp aaa.bbb.ccc.ddd      # This IP address 
  # TargetName  
   ConnectPort   25              # port to connect to target server 
   Secure        Yes             # Transport Layer Security (TLS) 
} 
...

Each CSSMTP instance reads from one nodeName and sends to one server. If you want to support more nodeNames you need more CSSMTP instances.

Certificates

As part of the TLS handshake, some servers will send down a certificate (to authenticate the server).

If your TLS provider, such as AT-TLS, uses keyrings, you need to import certificate into RACF, and connect the CA certificate to the keyring. Your keyring needs the CA for this certificate. You can get the servers certificate from the network flow, or from an openssl command.

If you are not using keyrings, for example Python uses openssl, you need the certificate in a file in Unix Services.

Getting my z/OS client to talk to a remote server over TLS

I looked into this because someone asked me about using the mail client on z/OS talking to a mail server somewhere in the internet.

My z/OS running on zPDT under Linux did not have a working connection to the outside world.

Configuring Linux to pass the z/OS traffic to the external network.

A ping to the server, got no response back.

Using Wireshark on the wireless connection, I could see my request from z/OS coming via Linux and going to the outside network. The source IP address was 10.1.1.1 – a “local only” address, and so the server could not send a response back to me.

The routing through Linux worked because I had a default route; ip route gave me

default via 192.168.xxx.xxx4 dev wlxd037450ab7ac proto dhcp metric 600 
10.1.0.0/24 dev eno1 proto kernel scope link src 10.1.0.3 metric 100 
10.1.0.0/24 via 10.1.0.3 dev eno1 proto static metric 100 
10.1.1.0/24 dev tap0 proto kernel scope link src 10.1.1.1 

To get the “right” IP address passed to the external network, was surprisingly easy. It was documented in the zPDT book.

I created a script

echo ‘Your firewall must be enabled for this command to be meaningful’
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -F FORWARD
iptables -P FORWARD ACCEPT
iptables -t nat -A POSTROUTING -o wlxd037450ab7ac -j MASQUERADE
# iptables -I INPUT -p tcp --dport 3270 -j ACCEPT

and used sudo to execute it.

I started the firewall

sudo ufw status
sudo ufw enable

I could then successfully ping the address

When I used Wireshark on the connection between Linux and z/OS, the source IP address was 10.1.1.2. When I used Wireshark on the wireless connection between Linux and the external network, the IP address was 192.168.xxx.xxx. This shows the effect of the iptables commands, it changed the 10.1.1.2 to a 192.168.xxx.xxx – and back again on the replies.

Getting the server’s certificate

I configured the AT-TLS definitions, for TLS support, specifying the choice of cipher specs I wanted to be used, and specified the keyring.

My z/OS client connected to the remote server. I could see the “client hello” flow, and the “server hello” response. In the server hello flow were the CA certificates the server supported. The client validates the certificates by checking the client’s key store.

I did not have the matching CA certificate in my keystore, and so my client failed to validate the certificate sent down, and the connection ended.

I could do it properly, and get the certificate from the official source for the server, but this was a test system, and I was happy to trust what was sent down in the handshake.

You can use wireshark or openssl to get the certificate

With openssl

openssl s_client -showcerts -tls1_2 -starttls smtp -connect smtp.gmail.com:587 | sed -ne ‘/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p’

You’ll need to interupt it (Ctrl-C) and paste the data into a file (google.pem)

You can display the contents of the certificate using

openssl x509 -in google.pem -text -noout|less

With Wireshark

display the certificates in the Server Hello flow.

right click on the certificate and use right click, export packet bytes. I created a file user.crt. This is a binary file.

The openssl command (on Linux)

openssl x509 -inform der -in user.crt -text > usercert.pem

displays the file in .pem format.

Put the certificate into the keyring

If you are using something that needs a keyring, you need to import the certificate into RACF and connect it to a keyring. If you are using something that uses openssl, such as Python, you need a .pem file in Unix Services.

I created a file on z/OS (VB) COLIN.USERCERT.PEM, and copied the .pem file into it (between and including the BEGIN CERTIFICATE and END CERTIFICATE lines).

I imported it, and connected it to my keyring using

//IBMRACF  JOB 1,MSGCLASS=H 
//S1  EXEC PGM=IKJEFT01,REGION=0M 
//SYSPRINT DD SYSOUT=*                                           
//SYSTSPRT DD SYSOUT=*                                           
//SYSTSIN DD *                                                   
RACDCERT CHECKCERT('COLIN.USERCERT.PEM')                         
RACDCERT DELETE  (LABEL('USERTRUST.ORG'))    CERTAUTH            
RACDCERT ADD('COLIN.USERCERT.PEM')        -                      
   CERTAUTH  WITHLABEL('USERTRUST.ORG') TRUST 
RACDCERT ID(START1) CONNECT(RING(TN3270) - 
                            CERTAUTH    - 
                            LABEL('USERTRUST.ORG')) 
SETROPTS RACLIST(DIGTCERT,DIGTRING ) refresh                     
                                                                 
RACDCERT LISTRING(TN3270)  ID(START1)                            
/*                                                              

Once I had refreshed my PAGENT job, and restarted my client code, I was able to establish a TLS session.

Client certificate

In the TLS handshake, the mail servers did not send down a request for the client certificate. As no certificate was requested, or sent up to the server, no certificate authentication was done. You need to check your mail server for it authentication process – it may just be userid and password. This information is sent once the TLS session has been established – so it flows encrypted.

Copying pages out of PDF files to make mini books.

I had been struggling trying to find content in some of the IBM pdf files (the same keyword is used in many places); and the online documentation is not very helpful in finding content.

I’ve found a brilliant way of cutting out the pages I need from a .pdf file.

I wanted to copy a pages 930-946 from a pdf file and create a new file. I used the pdftk package on Linux. PDF ToolKit = PDFK.

pdftk ~/apdf/TCP/IP_reference24.pdf cat 930-946 output tls.pdf

Easy!

Merging and extracting

You can do

pdftk R=IP_reference24.pdf C=IP_configguide2.4.pdf U=IP_usersGuide.pdf cat R1253-1295 C1309-1327 U325-338 output cssmtp.pdf

I can now merge bits of the reference book with bits of the configuration guide and bits of the usage guide to produce a pdf on one particular topic.

You can create a handle so the handle R is for the book IP_reference24.pdf. I use it in R1253-1295 which says copy pages 1253 to 1295 of the book handle “R”. Note this is the page number in the PDF – not the book’s page number. The book’s page number 1 is page 47 in the PDF, after the table of contents, list of figures, list of tables etc..

The description of pdftk is

DESCRIPTION
If PDF is electronic paper, then pdftk is an electronic staple-remover, hole-punch, binder, secret-decoder-ring, and X-Ray-glasses. Pdftk is a simple tool for doing everyday things with
PDF documents. Use it to:

  • Merge PDF Documents or Collate PDF Page Scans
  • Split PDF Pages into a New Document
  • Rotate PDF Documents or Pages
  • Decrypt Input as Necessary (Password Required)
  • Encrypt Output as Desired
  • Fill PDF Forms with X/FDF Data and/or Flatten Forms
  • Generate FDF Data Stencils from PDF Forms
  • Apply a Background Watermark or a Foreground Stamp
  • Report PDF Metrics, Bookmarks and Metadata
  • Add/Update PDF Bookmarks or Metadata
  • Attach Files to PDF Pages or the PDF Document
  • Unpack PDF Attachments
  • Burst a PDF Document into Single Pages
  • Uncompress and Re-Compress Page Streams
  • Repair Corrupted PDF (Where Possible)

z/OS PAGENT error messages and system SSL codes

Message

SYSERR :001: plfm_kernel_init: socket(INET, DGRAM, 0), failed, errno=EDC5112I Resource temporarily unavailable., errno2=74610296

OBJERR :001: init_PEP_and_kernel: Kernel initialization failed for image ‘TCPIP2’,

RACF profile EZB.INITSTACK.*.TCPIP2 CLASS(SERVAUTH) was missing.

//S1 EXEC PGM=IKJEFT01,REGION=0M
//STEPLIB DD DISP=SHR,DSN=SYS1.MIGLIB
//SYSPRINT DD SYSOUT=*
//SYSTSPRT DD SYSOUT=*
//SYSTSIN DD *
RDEFINE SERVAUTH EZB.INITSTACK..TCPIP2 PERMIT EZB.INITSTACK..TCPIP2 CLASS(SERVAUTH)
ID(START1) ACCESS(READ)
SETROPTS RACLIST(SERVAUTH) REFRESH
/*

WARNING:005: ..pinit_fetch_policy_profile: Ignoring non-image config file

In my image file I had a

TTLSRule
{

}

which is not allowed. They have to be in the TTLSConfig… file.

The error message was

WARNING:005: ..pinit_fetch_policy_profile: Ignoring non-image config file ‘TTLSRule’ statement

402 No SSL cipher specifications.

In some situations the cipher specs default.

You have to specify them for TLS 1.3

RC: Code 515 Initial handshake

Code 515 Key share list is not valid

I got this when trying to use TLS 1.3.

You need

TTLSSignatureParms
{
ClientKeyShareGroups 0023
ClientKeyShareGroups 0024
ClientKeyShareGroups 0025
ClientKeyShareGroups 0030

}

or ServerKeyShareGroup

517 No matches between elliptic curve and key share lists

Need something like

TTLSSignatureParms CPESigParms
{
CLientECurves 0023
CLientECurves 0024
CLientECurves 0025
CLientECurves 0029
ClientKeyShareGroups 0023
ClientKeyShareGroups 0024
ClientKeyShareGroups 0025
ClientKeyShareGroups 0029
}

519 Required ciphers have not been specified

I had TLS 1.3 specified, but no TLS 1.3 cipher specs.

Automation production of a series of charts in Excel format is easy with a bit of Python

We use a building, and have a .csv files of the power used every half hour for the last three months. We wanted to produce charts of the showing usage, for every Monday, and for every week throughout the year. Creating charts in a spreadsheet,manually creating a chart, and adding data to the series, soon got very boring. It was much more interesting to automate this. This blog post describes how I used Python and xlsxWwriter to create an Excel format spread sheet – all this from Linux.

Required output

Because our building is used by different groups during the week, I wanted to have

  • a chart for “Monday” for one group of users, “Tuesday” for another group of users, etc. This would allow me to see the typical profile, and make sure the calculated usage was sensible.
  • A chart on a week by week basis. So a sheet and chart for each week.
  • Automate this so, I just run a script to get the spread sheet and all of the graphs.

From these profiles we could see that from 0700 to 0900 every day there was a usage hump – a timer was turning on the outside lights, even though no one used the building before 1000!

Summary of the code

Reading the csv file

I used

import csv
fn = "HF.csv"
with open(fn, newline='') as csvfile:
    reader = csv.DictReader(csvfile)
   for row in reader:
      # get the column lables
      keys = row.keys()
...

Create the workbook and add a sheet

This opens the specified file chart_scatter.xlsx, for output, and overwrites any previous data.

import xlsxwriter
...
workbook = xlsxwriter.Workbook('chart_scatter.xlsx')
data= workbook.add_worksheet("Data")

Create a chart template

I used a Python function to create a standard chart with common configuration, so all charts had the same scale, and look and feel.

def default_chart(workbook,title):
   chart1 = workbook.add_chart({'type': 'scatter'})
   # Add a chart title and some axis labels.
   chart1.set_title ({'name': title})
   chart1.set_x_axis({
          'time_axis':  True,
          'num_format': 'hh:mm',
          'min': 0, 
          'max': 1.0,
          'major_unit':1/12., # 2 hours
          'minor_unit':1.0/24.0, # every hour
          'major_gridlines': {
            'visible': True,
            'line': {'width': 1.25, 'dash_type': 'long_dash'},
             },
          'minor_tick_mark': 'inside'
          })
   chart1.set_y_axis({
          'time_axis':  True,
          'min': 0, 
          'max': 7.0, # so they all have the same max value
          'major_unit':1,
          'minor_unit':0,
          'minor_tick_mark': 'inside'
          })
         #chart1.set_y_axis({'name': 'Sample length (mm)'})
   chart1.set_style(11)  # I do not know what this does
   chart1.set_size({'width': 1000, 'height': 700})
   return chart1

Create a chart for every day of the week

This creates a sheet (tab) for each day of the week, creates a chart, and attaches the chart to the sheet.

days=['Mon','Tue','Wed','Thu','Fri','Sat','Sun']
days_chart = []
for day in days:
      c=default_chart(day) # create chart
      days_chart.append(c)     # build up list of days
      # add a sheet with name of the day of the week 
      wb =workbook.add_worksheet(day) # create a sheet with name 
      wb.insert_chart('A1',c)  # add chart to sheet

Build up the first row of data labels as a header row

This processes the CSV file opened above and writes each key to the first row of the table.

In my program I had some logic to change the headers from the csv column name to a more meaningful value.

fn = "HF.csv"
with open(fn, newline='') as csvfile:
    reader = csv.DictReader(csvfile)
    # read the header row from the csv  
    row  = next(reader, None)
    count = LC.headingRow(workbook,data,summary,row)
    keys = list(row.keys())
    for i,j in enumerate(keys):
       #  'i' is is the position
       # 'j' is the value
       heading = j 
       # optional logic to change heading 
       # write data in row 0, column i
       data.write_string(0,i,heading) # first row an column of the data
    # add my own columns header
    data.write_string(0,count+1,"Daily total")      
    

Convert a string to a data time

d = row['Reading Date'] # 01/10/2022
dd,mm,yy  = d.split('/')
dt = datetime.fromisoformat(yy+'-'+mm+'-'+dd)
weekday = dt.weekday()	
# make a nice printable value
dow =days[weekday] + ' ' + dd + ' ' + mm + ' ' + yy
row['Reading Date'] = datetime.strptime(d,'%d/%m/%Y')

Write each row

This takes the data items in the CSV file and writes them a cell at a time to the spread sheet row.

I have some cells which are numbers, some which are strings, and one which is a date time. I have omitted the code to convert a string to a date time value

ddmmyy  = workbook.add_format({'num_format': 'dd/mm/yy'})
for row in reader:
    keys = row.keys()
    items = list(row.items())  
    for i,j  in enumerate(items):  # ith and (key,value)
       j =j[1] # get the value
       # depending on data type - used appropriate write method
       if isinstance(j,datetime):
          data.write_datetime(r,i, j,ddmmyy)
       else:
       if j[0].isdigit():  
           dec = Decimal(j)
           data.write_number(r,i,dec) 
           sum = sum + dec 
       else:    
          data.write(r,i ,j) 

Create a sheet for each week

 if (r == 1 or dt.weekday() == 6): # First record or Sunday
 # create a new work sheet, and chart 
    temp = workbook.add_worksheet(dd + '-' +mm)
    chart1 = workbook.add_chart({'type': 'scatter'})
    chart1 = default_chart('Usage for week starting '+ ...)
    # put chart onto the sheet
    temp.insert_chart('A1', chart1)   

Add data range to each chart

This says create a chart with

  • data name from the date value in column 3 of the row – r is row number
  • use the column header from data sheet row 0, column 5; to row 0 column count -1
  • use the vales from from r, column 5 to row r ,column count -1
  • pick the colour depending on the day colours[] is an array of colours [“red”,”blue”..]
  • picks a marker type based on week day from an array [“square”,”diamond”…]
# r is the row number in the data 
chart1.add_series({
         'name':       ['Data',r,3],
         #  field name is row 0 cols 5 to ... 
         'categories': ['Data',0,5,0,count-1],
          # data is in row r - same range 5 to  ,,,
         'values':     ['Data',r,5,r,count-1],
          # pick the colour and line width 
         'line':       {'color': colours[weekday],"width" :1 },
         # and the marker
         'marker':     {'type': markers[weekday]}
       })

Write a cell formula

You can write a formula instead of a value. You have to modify the formula for each row and column.

In a spread sheet you can create a formula, then use cut and paste to copy it to many cells. This will change the variables. If you have for cell A1, =SUM(A2:A10) then copy this to cell B2, the formula will be =SUM(B3:B11).

With xlsxWriter you have to explicitly code the formula

worksheet.write_formula('A1', '{=SUM(A2:A10)}')
worksheet.write_formula('B2', '{=SUM(B3:B11)}')

Save, clean up and end

I had the potential to hide columns – but then they did not display.

I made the column widths fit the data.

# hide boring stuff
# data.set_column('A:C',None,None,{'hidden': 1}) 
# Make columns narrow 
data.set_column('D:D', 5)  # Just Column d    
data.set_column('F:BA', 5)  # Columns F-BA 30.    
workbook.close()       
exit(0)

Colin’s “TCPIP on z/OS” message explanations

Purpose

This blog post is a repository of my interpretation of the messages from the Z/OS communications server family of products. Ive tried to add more information, or explain what some of the values are. it is aimed at search engines, not as a readable article.

EZZ7853I AREA LINK STATE DATABASE

This message can come from

  • OSPF external advertisements : The DISPLAY TCPIP,tcpipjobname,OMPROUTE,OSPF,EXTERNAL
  • OSPF area link state database: The DISPLAY TCPIP,tcpipjobname, OMPROUTE, OSPF, DATABASE, AREAID=area-id

in topic DISPLAY TCPIP,,OMPROUTE.

Type

  1. Router links advertisement
  2. Network links advertisements
  3. Network summaries
  4. Autonomous System(whole network) summaries
  5. Autonomous System(whole network) external advertisements (DISPLAY TCPIP, tcpipjobname, OMPROUTE, OSPF,EXTERNAL)

EZZ0318I HOST WAS FOUND ON LINE 8 AND FIRST HOP ADDRESS OR AN = WAS EXPECTED

I got this with

ROUTE 2001:db8::7/128 host 2001:db8:1::3    IFPORTCP6      MTU 5000 

Which has a first hop address! The problem was /128. Remove this and it worked. If you then issue TSO NETSTAT ROUTE it gives

DestIP:   2001:db8::7/128 
  Gw:     2001:db8:1::3 
  Intf:   IFPORTCP6         Refcnt:  0000000000 
  Flgs:   UGHS              MTU:     5000 

EZZ7904I Packet authentication failure, from 10.1.1.1, type 2

An OSPF packet of the specified type was received. The packet fails to authenticate.

System programmer response

Verify the authentication type and authentication key specified for the appropriate interfaces on this and the source router. The types and keys must match in order for authentication to succeed. If MD5 authentication is being used and OMPROUTE is stopped or recycled, ensure that it stays down for at least 3 times the largest configured dead router interval of the OSPF interfaces that use MD5 authenticaiton, in order to age out the authentication sequence numbers on routers that did not recycle.

Types are

  • 0 Null authentication
  • 1 Simple password
  • 2 Cryptographic authentication

See OSPF Version 2.

From the message description, this could be a timing issue.

EZZ7921I OSPF adjacency failure, neighbor 10.1.1.1, old state 128, new state 4, event 10

EZZ7921I.

I got this restarting frr on Linux.

The Neighbor State Codes can be one of the following:

  • 1 Down
  • 2 Attempt
  • 4 Init (session has (re) started
  • 8 2-way
  • 16 ExStart
  • 32 Exchange
  • 64 Loading
  • 128 Full. the router has sent and received an entire sequence of Database Description Packets.

The Neighbor Event Codes can be one of the following:

  • 7 SeqNumberMismatch
  • 8 BadLSReq
  • 10 1-way. An Hello packet has been received from the neighbor, inwhich this router is not mentioned. This indicates that communication with the neighbor is not bidirectional. For example the remote end is restarting.
  • 11 KillNbr
  • 12 InactivityTimer
  • 13 LLDown
  • 15 NoProg. This event is not described in RFC1583. This is an indication that adjacency establishment with the neighbor failed to complete in a reasonable time period (Dead_Router_Interval seconds). Adjacency establishment restarts.
  • 16 MaxAdj. This event is not described in RFC2328. This indicates that OMPROUTE has exceeded the futile neighbor state loop threshold (DR_Max_Adj_Attempt). Even if a redundant parallel interface (primary or backup) exists, OMPROUTE continues to attempt to establish adjacency with the same neighboring designated router over the existing or alternate interface.

EZZ7905I No matching OSPF neighbor for packet from 10.1.1.1, type 4

  • EZZ7905I No matching OSPF neighbor for packet from 10.1.1.1, type 4
  • EZZ7904I Packet authentication failure, from 10.1.1.1, type 2

I got these when I was using OSPF Authentication_type=MD5, and the Authentication_Key_ID did not match.

BPXF024I

You get messages prefixed by this message if SYSLOGD is not running.

For example

BPXF024I (TCPIP) Oct 6 10:11:10 omproute 67174435 : EZZ8100I OMPROUTE subagent Starting

With the SYSLOGD running you get

EZZ8100I OMPROUTE SUBAGENT STARTING

TELNET and AT-TLS

EZZ6035I TN3270 DEBUG CONN DETAIL 1035-00 Policy is invalid for the conntype specified.

EZZ6035I TN3270 DEBUG CONN DETAIL 
IP..PORT: 10.1.0.2..34588
CONN: 0000004E LU: MOD: EZBTTACP
RCODE: 1035-00 Policy is invalid for the conntype specified.
PARM1: PARM2: SECURE PARM3: POLICY NOT APPLCNTRL

POLICY NOT APPLCNTRL

The AT-TLS policy needs

TTLSEnvironmentAdvancedParms CSQ1-ENVIRONMENT-ADVANCED 
{ 
  ApplicationControlled         On 
...
}

Now you know, it is obvious that APPLCNTRL in the message means ApplicationControlled!

PARM2: SECURE PARM3: NO POLICY

EZZ6035I TN3270 DEBUG CONN   DETAIL                      
  RCODE: 1035-00  Policy is invalid for the conntype specified.      
  PARM1:          PARM2: SECURE   PARM3: NO POLICY                   

There is no AT-TLS policy for the port being used. The message does not tell you which port or policy is being used. The operator command “D TCPIP,TN3270,PROFILE” shows which ports are in use.

EZZ6060I TN3270 PROFILE DISPLAY 968                            
  PERSIS   FUNCTION        DIA  SECURITY    TIMERS   MISC      
 (LMTGCAK)(OPATSKTQSSHRTL)(DRF)(PCKLECXN23)(IPKPSTS)(SMLT)     
  L******  ***TSBTQ***RT*  TJ*  TSTTTT**TT  IP**STT  SMD*      
----- PORT:  2023  ACTIVE           PROF: CURR CONNS:      0   

The TS under security mean TLS connection, Secure Connection.

Use the Unix commands pasearch -t 1>a oedit a to display the configuration and search for “port”. The port value may be specified – or it may be within a range.

LocalPortFrom: 2023 LocalPortTo: 2025

EZZ6035I TN3270 RCODE: 1030-01 TTLS Ioctl failed for query or init HS.

PARM1: FFFFFFFF PARM2: 00000464 PARM3: 77B77221

The PARM1 value is the return value, the PARM2 value is the return code, and the PARM3 value is the reason code for the ioctl failure; these values are defined in z/OS UNIX System Services Messages and Codes.

  • Error numbers. 464 is ENOTCONN:The socket is not connected
  • Reason codes 7221: The connection was not in the proper state for retrieving.

I got this when

  • there was problems with the System SSL configuration, such as invalid certificate name,
  • when the z/OS certificate was not suitable eg the key needed to be bigger
  • the HandshakeRole ServerWithClientAuth was specified – it should be HandshakeRole Server .

EZZ6035I TN3270 DEBUG CONFIG EXCEPTION RCODE: 600F-00 System SSL initiation failed.


PARM1: 000000CA PARM2: 00000000 PARM3: GSK_ENVIRONMENT_INIT

AT-TLS did not have access to the keyring. For example need access to

RDEFINE RDATALIB START1.MQRING.LST UACC(NONE)
PERMIT START1.MQRING.LST CLASS(RDATALIB) ID(TCPIP) ACCESS(CONTROL)
tso setropts refresh raclist(rdatalib)

and perhaps access to

PERMIT IRR.DIGTCERT.LISTRING CLASS(FACILITY) ID(TCPIP) ACCESS(READ)

1030-02 – also to do with keyrings.

OMPRoute

EZZ7815I Socket 11 bind to port 521, address :: failed, errno=111:EDC5111I Permission denied., errno2=74637246

This was caused by

PORT
   520 UDP OMPROUTE            ; RouteD Server 
   521 UDP OMPROUTE            ; RouteD Server for IP V6 

The name after the UDP (OMPROUTE) did not match my job name which was trying to use it.

EZZ7811I COULD NOT ESTABLISH AFFINITY WITH INET, ERRNO=1011:

EDC8011I A NAME OF A PFS WAS SPECIFIED THAT EITHER IS NOT CONFIGURED OR IS NOT A SOCKETS PFS., ERRNO2=11B3005A

I had RESOLVER_CONFIG=//’ADCD.Z24C.TCPPARMS(TCPDATA)’ pointing to an invalid data set.

Getting the simplest OSPF network to work.

I struggled (and failed) to get OSPF routing to work with IPV6, so I tried with IP V4. This only took a couple of hours to get working. But I could not find any documentation which had baby steps to show you how it works, and what any output means.

This blog post is getting two Linux machines and z/OS to work with IPV4 and OSPF routing.

Some other blog articles give examples of the commands you can use to explore the configuration, and find what is running where.

What is OSPF

OSPF is a routing protocol where a router knows the topology of the network – rather than just the next hop. As the network changes, the changes are sent to the routers and their picture of the network is updated. OSPF scales to large number of routers.

My configuration

I used the frr (Free Range Routing) package which has routing capabilities for OSPF, OSPF6, RIP etc.

The laptop had

  • ip v4 address 10.1.0.2/24
  • routes
    • 10.1.0.0/24 dev enp0s31f6 proto kernel scope link src 10.1.0.2 metric 100
    • 10.1.0.0/24 via 10.1.0.2 dev enp0s31f6 proto static metric 100
    • 10.1.1.0/24 via 10.1.0.3 dev enp0s31f6
  • ospf router id 1.2.3.4

The server had

  • ip v4 address 10.1.0.3/24
  • routes
    • 10.1.0.0/24 dev eno1 proto kernel scope link src 10.1.0.3 metric 100
    • 10.1.0.0/24 via 10.1.0.3 dev eno1 proto static metric 100
  • ospf router-id 9.2.3.4

The z/OS system has

  • ip v4 address 10.1.1.2
  • routes
    • 10.1.0.0/24 via 10.1.1.1 on ETH1
    • 10.1.1.0/24 dev tap0 proto kernel scope link src 10.1.1.1
  • ospf router-id 10.1.1.2

Laptop frr.conf configuration file

The configuration file is described here.

frr version 7.2.1
frr defaults traditional
hostname laptop
log file /var/log/frr/frr.log
log timestamp precision 6

hostname laptop
service integrated-vtysh-config
...

!
interface enp0s31f6
 description colins ospf
 ip address 10.1.0.2 peer 10.1.0.3/24
 ip ospf area 0.0.0.0

!
router ospf
 ospf router-id 1.2.3.4

line vty

Server frr.conf configuration file

frr version 7.2.1
frr defaults traditional
hostname colin-ThinkCentre-M920s
log file /var/log/frr/frr.log
log timestamp precision 6
hostname Server
service integrated-vtysh-config

interface eno1
 description colins ospf
 ip address 10.1.0.3 peer 10.1.0.2/24
 ip ospf area 0.0.0.0

!
router ospf
 ospf router-id 9.2.3.4
!
line vty

z/OS configuration

TCPIP configuration file – defining ETH1

DEVICE PORTA  MPCIPA 
LINK ETH1  IPAQENET PORTA 
HOME 10.1.1.2 ETH1 
PORT 
   520 UDP OMP2                ; RouteD Server 
BEGINRoutes 
;     Destination   SubnetMask    FirstHop       LinkName  Size 

ROUTE 10.0.0.0    255.0.0.0           =        ETH1 MTU 1492 
ROUTE DEFAULT                     10.1.1.1     ETH1 MTU 1492 
ROUTE 10.1.0.0    255.255.255.0   10.1.1.1     ETH1 MTU 1492 
ROUTE 10.1.1.0    255.255.255.0       =        ETH1 MTU 1492 
ENDRoutes 
ITRACE OFF 
IPCONFIG NODATAGRAMFWD 
UDPCONFIG RESTRICTLOWPORTS 
TCPCONFIG RESTRICTLOWPORTS 
TCPCONFIG TTLS 
START PORTA 

JFPORTCP4 Interface configuration

This is in member USER.Z24C.TCPPARMS(jFACE41)

INTERFACE JFPORTCP4 
    DEFINE IPAQENET 
    CHPIDTYPE OSD 
    IPADDR 10.1.3.2 
    PORTNAME PORT2 

activate and start this using

v tcpip,tcpip,obeyfile,USER.Z24C.TCPPARMS(jFACE41) 

v tcpip,tcpip,sta,jfportcp4

OMPROUTE procedure

//OMPROUTE PROC 
// SET PO='POSIX(ON)' 
//OMPROUTE EXEC PGM=OMPROUTE,REGION=0M,TIME=NOLIMIT, 
// PARM=('&PO.,ENVAR("_CEE_ENVFILE_S=DD:STDENV")/ -6t2 -6d2') 
//OMPCFG DD DISP=SHR,DSN=USER.Z24C.TCPPARMS(&SYSJOBNM) 
//STDENV DD DISP=SHR,DSN=USER.Z24C.TCPPARMS(ENV&SYSJOBNM) 
//SYSPRINT DD SYSOUT=* 
//SYSOUT   DD SYSOUT=* 
//SYSTCPD DD DISP=SHR,DSN=ADCD.Z24C.TCPPARMS(TCPDATA) 
//CEEDUMP  DD SYSOUT=*,DCB=(RECFM=FB,LRECL=132,BLKSIZE=132) 
//  PEND 

and started with

S OMPROUTE,jobname=omp1

USER.Z24C.TCPPARMS(ENV&SYSJOBNM)

RESOLVER_CONFIG=//'ADCD.Z24C.TCPPARMS(TCPDATA)' 
OMPROUTE_DEBUG_FILE=/tmp/logs/omproute.debug 
OMPROUTE_IPV6_DEBUG_FILE=/tmp/logs/omprout6.debug 
OMPROUTE_DEBUG_FILE_CONTROL=1000,5 

OMPROUTE configuration USER.Z24C.TCPPARMS(OMP1)

ospf  RouterID=10.1.1.2; 
                                           
ospf_interface IP_address=10.1.1.2 
      name=ETH1 
      subnet_mask=255.255.255.0 
      ; 
ospf_interface IP_address=10.1.3.2 
      name=JFPORTCP4 
      subnet_mask=255.255.255.0 
      ; 

Startup joblog messages

EZZ7800I OMP1 STARTING
EZZ8171I OMP1 IPV4 OSPF IS USING CONFIGURED ROUTER ID 10.1.1.2 FROM OSPF STATEMENT
EZZ7898I OMP1 INITIALIZATION COMPLETE
EZZ8100I OMP1 SUBAGENT STARTING