#!/usr/bin/env python2

""" netlink_h.py: port of netlink.h public header
/*
 * netlink/netlink.h		Netlink Interface
 *
 *	This library is free software; you can redistribute it and/or
 *	modify it under the terms of the GNU Lesser General Public
 *	License as published by the Free Software Foundation version 2.1
 *	of the License.
 *
 * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
 */

Copyright (C) 2016  Dale V. Patterson (wraith.wireless@yandex.com)

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 3 of the License, or (at your option) any later
version.

Redistribution and use in source and binary forms, with or without modifications,
are permitted provided that the following conditions are met:
 o Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
 o Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
 o Neither the name of the orginal author Dale V. Patterson nor the names of any
   contributors may be used to endorse or promote products derived from this
   software without specific prior written permission.

A port of netlink.h, netlink/attr.h netlink/errno.h to python2

"""

__name__ = 'netlink_h'
__license__ = 'GPLv3'
__version__ = '0.0.3'
__date__ = 'March 2016'
__author__ = 'Dale Patterson'
__maintainer__ = 'Dale Patterson'
__email__ = 'wraith.wireless@yandex.com'
__status__ = 'Production'

import struct

NETLINK_ROUTE          =  0	# Routing/device hook
NETLINK_UNUSED         =  1	# Unused number
NETLINK_USERSOCK       =  2	# Reserved for user mode socket protocols
NETLINK_FIREWALL       =  3	# Unused number, formerly ip_queue
NETLINK_SOCK_DIAG      =  4	# socket monitoring
NETLINK_NFLOG          =  5	# netfilter/iptables ULOG
NETLINK_XFRM           =  6	# ipsec
NETLINK_SELINUX        =  7	# SELinux event notifications
NETLINK_ISCSI		   =  8	# Open-iSCSI
NETLINK_AUDIT          =  9	# auditing
NETLINK_FIB_LOOKUP     = 10
NETLINK_CONNECTOR      = 11
NETLINK_NETFILTER      = 12	# netfilter subsystem
NETLINK_IP6_FW         = 13
NETLINK_DNRTMSG        = 14	# DECnet routing messages
NETLINK_KOBJECT_UEVENT = 15	# Kernel messages to userspace
NETLINK_GENERIC        = 16
#leave room for NETLINK_DM (DM Events)
NETLINK_SCSITRANSPORT  = 18	# SCSI Transports
NETLINK_ECRYPTFS       = 19
NETLINK_RDMA           = 20
NETLINK_CRYPTO         = 21	# Crypto layer
__NETLINK_TYPE_MAX     = 22
NETLINK_TYPE_MAX       = __NETLINK_TYPE_MAX - 1

NETLINK_INET_DIAG = NETLINK_SOCK_DIAG

MAX_LINKS = 32

"""
struct nlmsghdr {
	__u32		nlmsg_len;	 /* Length of message including header */
	__u16		nlmsg_type;	 /* Message content */
	__u16		nlmsg_flags; /* Additional flags */
	__u32		nlmsg_seq;	 /* Sequence number */
	__u32		nlmsg_pid;	 /* Sending process port ID */
};
"""
nl_nlmsghdr = "IHHII"
NLMSGHDRLEN = struct.calcsize(nl_nlmsghdr)
def nlmsghdr(mlen,nltype,flags,seq,pid):
    """
     create a nlmsghdr
     :param mlen: length of message
     :param nltype: message content
     :param flags: additional flags
     :param seq: sequence number
     :param pid: process port id
     :returns: packed netlink msg header
    """
    return struct.pack(nl_nlmsghdr,NLMSGHDRLEN+mlen,nltype,flags,seq,pid)

# Flags values
NLM_F_REQUEST   =  1 # It is request message.
NLM_F_MULTI     =  2 # Multipart message, terminated by NLMSG_DONE
NLM_F_ACK       =  4 # Reply with ack, with zero or error code
NLM_F_ECHO      =  8 # Echo this request
NLM_F_DUMP_INTR = 16 # Dump was inconsistent due to sequence change

# Modifiers to GET request
NLM_F_ROOT   = 0x100 # specify tree	root
NLM_F_MATCH  = 0x200 # return all matching
NLM_F_ATOMIC = 0x400 # atomic GET
NLM_F_DUMP   = (NLM_F_ROOT|NLM_F_MATCH)

# Modifiers to NEW request
NLM_F_REPLACE = 0x100 # Override existing
NLM_F_EXCL    = 0x200 # Do not touch, if it exists
NLM_F_CREATE  = 0x400 # Create, if it does not exist
NLM_F_APPEND  = 0x800 # Add to end of list

"""
/*
   4.4BSD ADD		NLM_F_CREATE|NLM_F_EXCL
   4.4BSD CHANGE	NLM_F_REPLACE

   True CHANGE		NLM_F_CREATE|NLM_F_REPLACE
   Append		NLM_F_CREATE
   Check		NLM_F_EXCL
 */
"""

# Most netlink protocols enforce a strict alignment policy for all boundries.
# The alignment value is defined by NLMSG_ALIGNTO and is fixed to 4 bytes.
# Therefore all netlink message headers, begin of payload sections, protocol
# specific headers, and attribute sections must start at an offset which is a
# multiple of NLMSG_ALIGNTO.
#     <----------- nlmsg_total_size(len) ------------>
#     <----------- nlmsg_size(len) ------------>
#    +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
#    |  struct nlmsghdr  | Pad |     Payload    | Pad |  struct nlsmghdr  |
#    +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
#     <---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN ---
NLMSG_ALIGNTO = 4
def NLMSG_ALIGN(l): return (l+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1)
def NLMSG_LENGTH(l): return l+NLMSGHDRLEN
def NLMSG_SPACE(l): return NLMSG_ALIGN(NLMSG_LENGTH(l))
def NLMSG_ALIGNBY(l): return NLMSG_ALIGN(l) - l

NLMSG_NOOP     = 0x1 # Nothing.
NLMSG_ERROR    = 0x2 # Error
NLMSG_DONE     = 0x3 # End of a dump
NLMSG_OVERRUN  = 0x4 # Data lost

NLMSG_MIN_TYPE = 0x10 # < 0x10: reserved control messages

"""
struct nlmsgerr {
	int		error;
	struct nlmsghdr msg;
};
"""
nl_nlmsgerr = "hIHHII"
NLMSGERRLEN = struct.calcsize(nl_nlmsgerr)
NLMSGACKLEN = NLMSGHDRLEN + NLMSGERRLEN   # this is size of an error or ack message
def nlmsgerr(error,mlen,nltype,flags,seq,pid):
    """
     create a nlmsgerr
     NOTE: the function itself is here for illustrative purposes - users will
     only need the format string above to unpack these
     :param error: error code
     :param mlen: length of header
     :param nltype: message content
     :param flags: additional flags
     :param seq: sequence number
     :param pid: process port id
     :returns: packed netlink msg error
    """
    return struct.pack(nl_nlmsgerr,error,mlen,nltype,flags,seq,pid)

NETLINK_ADD_MEMBERSHIP  = 1
NETLINK_DROP_MEMBERSHIP = 2
NETLINK_PKTINFO         = 3
NETLINK_BROADCAST_ERROR = 4
NETLINK_NO_ENOBUFS      = 5
NETLINK_RX_RING         = 6
NETLINK_TX_RING         = 7

# nume nl_nmap_status
NL_MMAP_STATUS_UNUSED   = 0
NL_MMAP_STATUS_RESERVED = 1
NL_MMAP_STATUS_VALID    = 2
NL_MMAP_STATUS_COPY     = 3
NL_MMAP_STATUS_SKIP     = 4

NET_MAJOR = 36 # Major 36 is reserved for networking

NETLINK_UNCONNECTED = 0
NETLINK_CONNECTED   = 1

"""
/*
* netlink/attr.h               Netlink Attributes
 *
 *      This library is free software; you can redistribute it and/or
 *      modify it under the terms of the GNU Lesser General Public
 *      License as published by the Free Software Foundation version 2.1
 *      of the License.
 *
 * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
 *
 *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
 * +---------------------+- - -+- - - - - - - - - -+- - -+
 * |        Header       | Pad |     Payload       | Pad |
 * |   (struct nlattr)   | ing |                   | ing |
 * +---------------------+- - -+- - - - - - - - - -+- - -+
 *  <-------------- nlattr->nla_len -------------->
 */
"""

"""
 I add two datatypes to the netlink definition:
 NLA_ERROR: designates an attribute that failed during unpacking
 NLA_SET_*: designates an array of like sized attributes Sets are similar to nested
  attributes but are not seperated by pad bytes.
"""
NLA_DATATYPES = ['unspec','u8','u16','u32','u64','string','flag','msecs','nested',
                 'set_u8','set_u16','set_u32','set_u64']
NLA_ERROR      = -1 # my own -> failed to unpack attribute, treat as unspec
NLA_UNSPEC     =  0	# Unspecified type, binary data chunk
NLA_U8         =  1  # 8 bit integer
NLA_U16        =  2	# 16 bit integer
NLA_U32        =  3	# 32 bit integer
NLA_U64        =  4	# 64 bit integer
NLA_STRING     =  5	# NUL terminated character string
NLA_FLAG       =  6	# Flag
NLA_MSECS      =  7	# Micro seconds (64bit)
NLA_NESTED     =  8	# Nested attributes
# the below are my own added to support nl80211 data like cipher_suites
NLA_SET_U8     =  9 # set of u8s
NLA_SET_U16    = 10 # set of u16s
NLA_SET_U32    = 11 # set of u32s
NLA_SET_U64    = 12 # set of u64s
NLA_TYPE_MAX   =  NLA_SET_U64

"""
struct nlattr {
	__u16           nla_len; # length of attribute + nlattr size
	__u16           nla_type;
};
"""
nl_nlattrhdr = "HH"
NLATTRHDRLEN = struct.calcsize(nl_nlattrhdr)
def nlattrhdr(alen,atype):
    """
     create a nlattr
     :param alen: length of attribute
     :param atype: type of attribute
     return packed netlink attribute
    """
    return struct.pack(nl_nlattrhdr,alen+NLATTRHDRLEN,atype)

"""
/*
 * nla_type (16 bits)
 * +---+---+-------------------------------+
 * | N | O | Attribute Type                |
 * +---+---+-------------------------------+
 * N := Carries nested attributes
 * O := Payload stored in network byte order
 *
 * Note: The N and O flag are mutually exclusive.
 */
"""
NLA_F_NESTED		= (1 << 15)
NLA_F_NET_BYTEORDER	= (1 << 14)
NLA_TYPE_MASK		= ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)

# defined error codes
# only use success and failure -> using errno for other error numbers
NLE_SUCCESS           =  0
NLE_FAILURE           =  1