Get IP address by subnet in Puppet - puppet

I have a couple machines, each with multiple network interfaces:
lead$ ip addr
2: enp0s2: ...
inet 10.1.1.11/24 brd 10.1.1.255 scope global enp0s2
3: enp0s3: ...
inet 10.2.2.11/24 brd 10.2.2.255 scope global enp0s3
iron$ ip addr
2: enp0s99: ...
inet 10.1.1.12/24 brd 10.1.1.255 scope global enp0s99
3: enp0s3: ...
inet 10.2.2.12/24 brd 10.2.2.255 scope global enp0s3
Note that on lead, 10.1.1.0/24 is on enp0s2, but on
iron, 10.1.1.0/24 is on enp0s99.
In Puppet, how would I get the IP address(es) (or interface name)
corresponding to the subnet 10.1.1.0/24? Using $::ipaddress_enp0s2
clearly won't work, as the interface name is different across machines.
What I want is something like $::ipaddress_10_1_1_0 (with the value
10.1.1.11 on lead and the value 10.1.1.12 on iron).
For reference: In Ansible, I would do something like:
- shell: "ip route get 10.1.1.0 | awk '{print $6}'"
register: ipaddr
- debug:
msg: "{{ ipaddr.stdout }} is my IP on 10.1.1.0/24"

You will need to use the same approach in Puppet, using custom facts to create your own.

Puppet Labs actually has an implementation of exactly this in their puppetlabs-openstack module:
require "ipaddr"
module Puppet::Parser::Functions
newfunction(:ip_for_network, :type => :rvalue, :doc => <<-EOS
Returns an ip address for the given network in cidr notation
ip_for_network("127.0.0.0/24") => 127.0.0.1
EOS
) do |args|
addresses_in_range = []
range = IPAddr.new(args[0])
facts = compiler.node.facts.values
ip_addresses = facts.select { |key, value| key.match /^ipaddress/ }
ip_addresses.each do |pair|
key = pair[0]
string_address = pair[1]
ip_address = IPAddr.new(string_address)
if range.include?(ip_address)
addresses_in_range.push(string_address)
end
end
return addresses_in_range.first
end
end
Rather than making a custom fact, this loops over the existing facts and finds ones that look like IP addresses.

Related

IP addressing of the equipment behind the router for SNMP

I am trying to develop a Traps receiver for SNMPv1 and SNMPv2c with OpenVPN. I have used the PURESNMP and PYSNMP libraries with Python. PYSNMP can receive SNMPv1 and SNMPv2c Traps, while PURESNMP only works with SNMPv2c. My project works as follows: the OpenVPN file is loaded into a Router so that it can connect, and several devices are connected behind the Router to be monitored by SNMP.
OpenVPN Router IP: 10.8.0.18,
LAN Router IP: 192.168.2.1
Team A IP: 192.168.2.3
OpenVPN Server IP: 10.8.0.1
Client2 IP: 10.8.0.10
I manage to receive the Traps, but I cannot distinguish where the information comes from, if in the LAN behind the Router how is it possible to identify from which the alarm came. This is the Team's SNMPv1 Trap response:
Agent is listening SNMP Trap on 10.8.0.10 , Port : 162
-------------------------------------------------- ------------------------
SourceIP: ('10.8.0.18', 49181)
----------------------Received new Trap message------------------------ ---
1.3.6.1.2.1.1.3.0 = 910296
1.3.6.1.6.3.1.1.4.1.0 = 1.3.6.1.4.1.1918.2.13.0.700
1.3.6.1.6.3.18.1.3.0 = 10.168.2.3
1.3.6.1.6.3.18.1.4.0 = public
1.3.6.1.6.3.1.1.4.3.0 = 1.3.6.1.4.1.1918.2.13
1.3.6.1.4.1.1918.2.13.10.111.12.0 = 1
1.3.6.1.4.1.1918.2.13.10.111.10.0 = 3
1.3.6.1.4.1.1918.2.13.10.111.11.0 = Digital Input 1
1.3.6.1.4.1.1918.2.13.10.111.14.0 = 4
1.3.6.1.4.1.1918.2.13.10.10.40.0 = System Location
1.3.6.1.4.1.1918.2.13.10.10.50.0 = SiteName
1.3.6.1.4.1.1918.2.13.10.10.60.0 = SiteAddress
1.3.6.1.4.1.1918.2.13.10.111.13.0 = Input D01 Disconnected.
In “IP Source”, a function is used to obtain the source IP, but it shows me the IP of the Router and not of the device that is alarmed. If you look at the third line of the traps, it indicates a source IP that the SNMPV1 protocol itself has incorporated:
1.3.6.1.6.3.18.1.3.0 = 10.168.2.3
But it is not from the device or from the Router, it is as if the Router's network segment had been mixed with the device's hots segment. This is the response when receiving SNMPv2c Traps:
Agent is listening SNMP Trap on 10.8.0.10 , Port : 162
-------------------------------------------------- --------------------------
SourceIP: ('10.8.0.18', 49180)
----------------------Received new Trap message------------------------ -----
1.3.6.1.2.1.1.3.0 = 896022
1.3.6.1.6.3.1.1.4.1.0 = 1.3.6.1.4.1.1918.2.13.20.700
1.3.6.1.4.1.1918.2.13.10.111.12.0 = 1
1.3.6.1.4.1.1918.2.13.10.111.10.0 = 3
1.3.6.1.4.1.1918.2.13.10.111.11.0 = Digital Input 1
1.3.6.1.4.1.1918.2.13.10.111.14.0 = 5
1.3.6.1.4.1.1918.2.13.10.10.40.0 = System Location
1.3.6.1.4.1.1918.2.13.10.10.50.0 = SiteName
1.3.6.1.4.1.1918.2.13.10.10.60.0 = SiteAddress
1.3.6.1.4.1.1918.2.13.10.111.13.0 = Input D01 Disconnected.
In Trap SNMPv2c, you can only get the source IP but it shows the IP of the Router, but it does not work for me since there are several devices behind the Router, and there is no way to identify which one the alarm came from. I am doing the tests from an OpenVPN client, and not yet from the server, it will be uploaded to the server once it works well.
Could you help me because that can happen. Since I thought it was a problem with the libraries and I used another trap receiving software and the answer was the same.
This is the Server configuration:
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh none
server 10.8.0.0 255.255.0.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
client-config-dir ccd
route 192.168.0.0 255.255.0.0
keepalive 10 120
tls-crypt ta.key
cipher AES-256-GCM
auth SHA256
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
verb 3
explicit-exit-notify 1
This is the client configuration:
client
dev tun
proto udp
remote x.x.x.x 1194
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun
remote-cert-tls server
cipher AES-256-GCM
auth SHA256
key-direction 1
verb 3
<ca>
</ca>
<cert>
</cert>
<key>
</key>
<tls-crypt>
#
#
</tls-crypt>
Firewall configuration on the server:
# START OPENVPN RULES
# NAT table rules
*nat
:POSTROUTING ACCEPT [0:0]
# Allow traffic from OpenVPN client to eth0 (change to the interface you discovered!)
-A POSTROUTING -s 10.8.0.0/8 -o eth0 -j MASQUERADE
COMMIT
# END OPENVPN RULES
This is the code to Receive SNMPv1 and SNMPv2c Traps:
from pysnmp.carrier.asynsock.dispatch import AsynsockDispatcher
#python snmp trap receiver
from pysnmp.entity import engine, config
from pysnmp.carrier.asyncore.dgram import udp
from pysnmp.entity.rfc3413 import ntfrcv
from pysnmp.proto import api
datosnmp = []
snmpEngine = engine.SnmpEngine()
TrapAgentAddress='10.8.0.10' #Trap listerner address
Port=162 #trap listerner port
print("Agent is listening SNMP Trap on "+TrapAgentAddress+" , Port : " +str(Port))
print('--------------------------------------------------------------------------')
config.addTransport(snmpEngine, udp.domainName + (1,), udp.UdpTransport().openServerMode((TrapAgentAddress, Port)))
#Configure community here
config.addV1System(snmpEngine, 'my-area', 'public')
def cbFun(snmpEngine, stateReference, contextEngineId, contextName,varBinds, cbCtx):
global datosnmp
#while wholeMsg:
execContext = snmpEngine.observer.getExecutionContext('rfc3412.receiveMessage:request')
print("IP Source: ", execContext['transportAddress']) #IP Origen del Trap
#print('snmpEngine : {0}'.format(snmpEngine))
#print('stateReference : {0}'.format(stateReference))
#print('contextEngineId : {0}'.format(contextEngineId))
#print('contextName : {0}'.format(contextName))
#print('cbCtx : {0}'.format(cbCtx))
print('{0}Received new Trap message{0}\n'.format('-' * 40))
for oid, val in varBinds:
datosnmp.append(val.prettyPrint())
print('%s = %s' % (oid.prettyPrint(), val.prettyPrint())) #name = OID, val = contenido de la OID
#print(datosnmp)
ntfrcv.NotificationReceiver(snmpEngine, cbFun)
snmpEngine.transportDispatcher.jobStarted(1)
try:
snmpEngine.transportDispatcher.runDispatcher()
except:
snmpEngine.transportDispatcher.closeDispatcher()
raise
Could you please help me if it could be a configuration error of the OpenVPN server, or maybe something else needs to be added. Have you seen a similar lake?
Any comment is appreciated.

How to enable VRF on startup on Linux/Systemd/Networkd

Using systemd-networkd I've created two VRF :
File : /etc/systemd/network/vrf20.netdev
[NetDev]
Name=vrf-mpls-red
Kind=vrf
[VRF]
Table=20
File : /etc/systemd/network/vrf30.netdev
[NetDev]
Name=vrf-mpls-green
Kind=vrf
[VRF]
Table=30
Each VRF have some network interfaces associated.
After system startup, both VRF are "DOWN" :
3: vrf-mpls-red: <NOARP,MASTER> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/ether fe:ab:58:be:29:ab brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 1280 maxmtu 65535
vrf table 20 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
4: vrf-mpls-green: <NOARP,MASTER> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/ether 26:a8:71:a0:9b:b2 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 1280 maxmtu 65535
vrf table 30 numtxqueues 1 numrxqueues 1 gso_max_size 65536 gso_max_segs 65535
File : `/etc/systemd/network/vrf20.netdev`
I have to use ip link set dev vrf-mpls-red and ip link set dev vrf-mpls-green to have VRF "UP" and to have network communication between interfaces inside the same VRF.
How to configure networkd to automaticaly put VRF "UP"?
You need .network files to pair for your .netdev
For example 99-vrf.network with:
[Match]
Name=vrf-*
[Link]
ActivationPolicy=up
RequiredForOnline=no
Will bring up both your vrf. Also be accuracy with file naming, always use 00- 10- 90- in the start of name.

Why are there multiple IP addresses for a IP object?

In scapy, I see the following.
>>> a=IP(dst="www.slashdot.org/30")
>>> [p for p in a]
[<IP dst=216.105.38.12 |>,
<IP dst=216.105.38.13 |>,
<IP dst=216.105.38.14 |>,
<IP dst=216.105.38.15 |>]
But I don't see multiple targets with dig. Does anybody know why is it so? What is the "/30" in the above IP command mean?
$ dig www.slashdot.org a +noall +answer
www.slashdot.org. 384 IN A 216.105.38.15
Subnet Math
The /30 refers to CIDR notation. It is equivalent to a subnet mask of 255.255.255.252.
Essentially it's a mask to determine which bits of the IPv4 address are network bits and host bits.
/30 and 216.105.38.15 in binary are
11111111.11111111.11111111.11111100
11011000.01101001.00100110.00001111
To get the network address, you use a binary & to get 216.105.38.12. This subnet consists of all combinations of addresses where the host bits are variable. So these last two bits can be 00, 01, 10, or 11 (i.e. 0, 1, 2, 3). This translates to the .12, .13, .14, .15 we see scapy output.
Scapy classes
Per the scapy IP class (scapy.layers.inet.IP), when you input a subnet for the dest IP (scapy.layers.inet.DestIPField) with dst=, it's interpreted as a subnet (scapy.base_classes.Net), and all addresses in the subnet are returned.
So I will get the same result if I pass the subnet to the Net class.
>>> from scapy.base_classes import Net # if in Python and not Scapy
>>> a = Net("www.slashdot.net/30")
>>> [p for p in a]
['216.105.38.12', '216.105.38.13', '216.105.38.14', '216.105.38.15']

snmp cisco switch oid for multiVlans

I'm using snmp to go through a switch and get some info.
i use:
for searching Names :
Oid: iso.3.6.1.2.1.2.2.1.2
for searching a Single Vlan:
Oid: iso.3.6.1.4.1.9.9.68.1.2.2.1.2
What i want is for multiple Vlans what is the oid.
Does anyone knows?
The Cisco OID referred to in your post will give you the VLAN-id associated on each interface on the device. For example snmpwalk yields following (so you can get all VLANs associated with the interfaces on the device)
# snmpwalk -v2c -c public 172.23.219.36 1.3.6.1.4.1.9.9.68.1.2.2.1.2
..
SNMPv2-SMI::enterprises.9.9.68.1.2.2.1.2.436217344 = INTEGER: 101
Taking ifIndex from VLAN 101 one can find the associated interface
# snmpwalk -v2c -c public 172.23.219.36 1.3.6.1.2.1.2.2.1.2.436217344
..
IF-MIB::ifDescr.436217344 = STRING: Ethernet1/20
However if you are only interested in getting VLANs on the system you may want to consider looking at Q-BRIDGE-MIB (https://www.rfc-editor.org/rfc/rfc4363). Any of these OIDs will get all the VLANs on the system whether associated with an interface or not via Q-BRIDGE MIB (dot1qFdbTable)
snmpwalk -v2c -c public 172.23.219.36 1.3.6.1.2.1.17.7.1.2
SNMPv2-SMI::mib-2.17.7.1.2.1.1.2.1 = Counter32: 0
SNMPv2-SMI::mib-2.17.7.1.2.1.1.2.101 = Counter32: 0
SNMPv2-SMI::mib-2.17.7.1.2.1.1.2.201 = Counter32: 0
SNMPv2-SMI::mib-2.17.7.1.2.1.1.2.301 = Counter32: 0

Configure local virtual network with multihoming

I need to test an application with more than 64K connections. I want to use the same host.
Today I start server that listents on 127.0.0.1 and connect to it from same machine, but of course it is limited to 64K connections.
I want to simulate a situation like I have one server and many clients connecting to a single server on specific single IP.
Server Listen: 1.2.3.4
Client Connect to 1.2.3.4 From 2.1.2.1
Client Connect to 1.2.3.4 From 2.1.2.2
Client Connect to 1.2.3.4 From 2.1.2.3
Client Connect to 1.2.3.4 From 2.1.2.4
So I need to setup a virtual network with multihoming so the client would be able to connect from several addressed and a server that listen on.
How can this be configured? On Linux?
Step 1: Add addresses to your loopback; I will add 10.0.0.1, 10.0.0.2, 10.0.0.3, 10.0.0.4 and 10.0.0.5 to lo
[mpenning#tsunami ~]$ ip addr show lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
[mpenning#tsunami ~]$ sudo ip addr add 10.0.0.1/24 dev lo
[mpenning#tsunami ~]$ sudo ip addr add 10.0.0.2/24 dev lo
[mpenning#tsunami ~]$ sudo ip addr add 10.0.0.3/24 dev lo
[mpenning#tsunami ~]$ sudo ip addr add 10.0.0.4/24 dev lo
[mpenning#tsunami ~]$ sudo ip addr add 10.0.0.5/24 dev lo
[mpenning#tsunami ~]$ sudo ip addr show lo
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet 10.0.0.1/24 scope global lo
inet 10.0.0.2/24 scope global secondary lo
inet 10.0.0.3/24 scope global secondary lo
inet 10.0.0.4/24 scope global secondary lo
inet 10.0.0.5/24 scope global secondary lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
[mpenning#tsunami ~]$
Step 2: Increase max connections
Step 3: Start lots of connections...
from socket import socket, AF_INET, SOCK_STREAM
import select
class Server(object):
def __init__(self, addr="0.0.0.0", port=2525):
self.listen_addr = addr
self.listen_port = port
self.server = socket(AF_INET, SOCK_STREAM)
self.server.setblocking(0)
self.server.bind(self.socket_tuple)
self.server.listen(5)
def __repr__(self):
return "TCP Server listening on %s:%s" % (self.listen_addr,
self.listen_port)
#property
def socket_tuple(self):
return (self.listen_addr, self.listen_port)
def close(self):
self.server.close()
class Client(object):
def __init__(self, addr="0.0.0.0"):
self.listen_addr = addr
self.server_addr = None
self.server_port = None
self.client = socket(AF_INET, SOCK_STREAM)
self.client.setblocking(0)
finished = False
while not finished:
try:
### The point of my answer is here...
self.client.bind((addr,0)) # <- Bind to specific IP and rnd port
finished = True
except:
pass
def __repr__(self):
return "TCP Client %s->%s:%s" % (self.listen_addr,
self.server_addr, self.server_port)
def connect(self, socket_tuple=("0.0.0.0",0)):
self.server_addr = socket_tuple[0]
self.server_port = socket_tuple[1]
self.client.connect_ex(socket_tuple)
def close(self):
self.client.close()
READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR
clients = list()
servers = list()
for server_addr, port in [('10.0.0.1', 2525), ('10.0.0.2', 2526)]:
ss = Server(addr=server_addr, port=port)
servers.append(ss)
for client_addr in ['10.0.0.3', '10.0.0.4', '10.0.0.5']:
for ii in xrange(0, 25000):
cc = Client(addr=client_addr)
connection = cc.connect(socket_tuple=ss.socket_tuple)
finished = False
print " %s conns" % len(clients)
while not finished:
poller = select.poll()
poller.register(ss.server, READ_ONLY)
# Map file descriptors to socket objects...
fd_to_socket = {ss.server.fileno(): ss.server,}
events = poller.poll(1)
for fd, flag in events:
s = fd_to_socket[fd]
if flag & (select.POLLIN|select.POLLPRI):
if s is ss.server:
# server socket is ready to accept a connection
connection, client_address = s.accept()
connection.setblocking(0)
fd_to_socket[connection.fileno()] = connection
poller.register(connection, READ_ONLY)
finished = True
clients.append(cc)
print cc
print "Total Clients:", len(clients)
for cc in clients:
cc.close()
for ss in servers:
ss.close()
Adjust values above to your preferences... however, you should keep in mind that tuning a linux server to accept so many connections may be challenging... but at this point, that isn't the question you are asking...

Resources