pseudowire 8.19 KB
Newer Older
Alexander Gall's avatar
Alexander Gall committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Snabb::SNMP::Agent qw(%persistent_ifIndex %compound_scalar_handlers);
my %opt = ( 'check-interval' => 5,
            'mibs-dirs' => '',
            'shmem-dir' => '/tmp/snabb-shmem');

sub usage() {
  print <<"EOF";
usage: $0 --ifindex=<file>
EOF
  exit(1);
}

## Mappings of PW shared memory segments to the index of the
## pwTable/cpwVcTable
my %pwIndex;
my $pwIndex = 1;

## Indexer for pwTable/cpwVcTable.  It uses the name of the segment
## that represents a row in these tables to generate the index.
sub pw_indexer($$$) {
  my ($oid, $table_oid, $segment) = @_;
  my $name = $segment->{name};
  my $index = $pwIndex{$name};
  unless (defined $index) {
    $index = $pwIndex++;
    print("Allocating PW index $index for segment $name\n");
    $pwIndex{$name} = $index;
  }
  return $oid.".".$index;
}

## Indexer for the pwEnetTable.  It has two indexes.  The first is the
## pwIndex according to the pw_indexer.  the second is the value of
## the pwEnetPwInstance object.
sub pw_enet_indexer($$$) {
  my ($oid, $table_oid, $segment) = @_;
  $oid = pw_indexer($oid, $table_oid, $segment);
  my $pwEnetPwInstance_obj = $segment->{objs}{pwEnetPwInstance};
  defined $pwEnetPwInstance_obj or
    die "pw_enet_indexer: no pwEnetPwInstance present for segment "
      ."$segment->{name}";
  tie my $pwEnetPwInstance, 'Snabb::SNMP::Tie::INTEGER', $segment,
    'pwEnetPwInstance';

  ##print("XXX $pwEnetPwInstance\n");
  return $oid.".".$pwEnetPwInstance;
}

## Indexer for the cpwVcEnetTable.  It has two indexes.  The first is
## the cpwVcIndex according to the pw_indexer.  the second is the value
## of the cpwVcEnetPwVlan object.
sub cpw_enet_indexer($$$) {
  my ($oid, $table_oid, $segment) = @_;
  $oid = pw_indexer($oid, $table_oid, $segment);
  my $cpwVcEnetPwVlan_obj = $segment->{objs}{cpwVcEnetPwVlan};
  defined $cpwVcEnetPwVlan_obj or
    die "cpw_enet_indexer: no cpwVcEnetPwVlan present for segment "
      ."$segment->{name}";
  tie my $cpwVcEnetPwVlan, 'Snabb::SNMP::Tie::INTEGER', $segment,
    'cpwVcEnetPwVlan';

  ##print("XXX $cpwVcEnetPwVlan\n");
  return $oid.".".$cpwVcEnetPwVlan;
}

### Definition of the subtrees that will be registered to the master
### agent.  In the following, the term "object" refers to a string
### which is either a literal OID (in dotted notation) or a name that
### can be translated to an OID through the loaded MIBs (via
### %SNMP::MIB).
###
### The keys of %subtrees are objects that designate the subtrees that
### will be registered with the master agent.  Each subtree may
### contain two hashes named "handlers" and "tables".  The keys of
### these hashes are objects which must be part of the subtree.
### Subtrees must not overlap.
###
### The "tables" hash must contain a key called "indexer", which must
### be a reference to a function that is able to create the full
### instance ID (IID) for any object in the table.
###
### The subtrees are populated with objects from memory segments
### shared with Snabb instances that use the lib.ipc.shmem mechansim
### with the name space "MIB" as follows.
###
### Once an object is read from the index file of a shared memory
### segment, a lookup in the subtree hash is performed to find the
### subtree that contains it.  If no match is found, the object is
### ignored.  Otherwise, the object is matched against all tables
### which are registered in the "tables" hash of the subtree.  If the
### object is not covered by any table, it is considered to be a
### scalar object and the IID is constructed from the object's OID by
### adding the index ".0".  Otherwise, the object is considered to be
### part of the table and its indexer function is called with the OID
### of the object, the base OID of the table and a reference to a hash
### that describes the segment.  The indexer returns the IID of the
### object.
###
### The value that will be returned for a query for the IID is
### generated as follows.  The data type of the object is obtained
### from the MIB by referencing the "type" field of the OID node
### returned from a lookup in %SNMP::MIB.  The type is associated with
### a class of the Snabb::SNMP::Tie package via the hash %class_map.
### A scalar variable is then tied to this class, passing a reference
### to the segment descriptor and the name of the object and possibly
### a "handler".  The purpose of the handler is to apply
### object-specfic manipulations to the value obtained from the shared
### segment before passing it on to the master agent.
###
### The handler of an object is determined as follows.  If the object
### is a scalar, it is looked up in the "handlers" hash of its
### subtree.  If it is part of a table, the lookup is done in the
### "handlers" hash of the table instead.  If the lookup fails, the
### tied scalar uses no handler.  If the lookup succeeds, the
### corresponding value is interpreted as a reference to a function
### and associated with the tied variable.
###
### Finally, the IID is registered in the master MIB table %mibs as a
### hash that contains the keys "type" and "value", where the type is
### the data type of the object (more precisely, the type translated
### through the hash %type_tr) and the value is a reference to the
### tied value.
###
### When a request for the IID is received, the tied variable is
### dereferenced to obtain the object's value.  Essentially, the Tie
### class will read the raw value from the shared memory segment and
### transform it to the proper type.  If the object is associated with
### a handler, the handler is called with the value and a reference to
### the segment descriptor to apply any special processing.  Finally,
### the resulting value will be stored in the sub-agent's PDU and
### handed back to the master agent.
my %subtrees =
  ( cpwVcMIB =>
    { tables =>
      { scalars =>
	{ handlers =>
	  { cpwVcIndexNext =>
	    { compound_handler => sub { return $pwIndex; } },
	    cpwVcPerfTotalErrorPackets =>
	    { compound_handler => $compound_scalar_handlers{accumulator} },
	  },
	},

	cpwVcTable =>
	{ indexer => \&pw_indexer,
	},
      },
    },

    pwStdMIB =>
    { tables =>
      { scalars =>
	{ handlers =>
	  { pwIndexNext =>
	    { compound_handler => sub { return $pwIndex; } },
	    pwPerfTotalErrorPackets =>
	    { compound_handler => $compound_scalar_handlers{accumulator} },
	  },
	},

	pwTable =>
	{ indexer => \&pw_indexer,
	},
      },
    },

    cpwVcEnetMIB =>
    { tables =>
      { cpwVcEnetTable =>
	{ indexer => \&cpw_enet_indexer,
	  handlers =>
	  { cpwVcEnetPortIfIndex =>
	    { handler => sub {
		my ($value, $name, $segment) = @_;
		my $aux_name = "_X_$name";
		exists $segment->{objs}{$aux_name} or die
		  "$name handler: auxiliary object $aux_name does not exist";
		tie my $ifDescr, 'Snabb::SNMP::Tie::OCTETSTR', $segment,
		  $aux_name;
		my $ifIndex = $persistent_ifIndex{$ifDescr};
		defined $ifIndex or die
		  "$name handler: unknown interface $ifDescr";
		return($ifIndex);
	      }
	    },
	  },
	},
      },
    },

    pwEnetStdMIB =>
    { tables =>
      { pwEnetTable =>
	{ indexer => \&pw_enet_indexer,
	  handlers =>
	  { pwEnetPortIfIndex =>
	    { handler => sub {
		my ($value, $name, $segment) = @_;
		my $aux_name = "_X_$name";
		exists $segment->{objs}{$aux_name} or die
		  "$name handler: auxiliary object $aux_name does not exist";
		tie my $ifDescr, 'Snabb::SNMP::Tie::OCTETSTR', $segment,
		  $aux_name;
		my $ifIndex = $persistent_ifIndex{$ifDescr};
		defined $ifIndex or die
		  "$name handler: unknown interface $ifDescr";
		return($ifIndex);
	      }
	    },
	  },
	},
      },
    },

  );

GetOptions(\%opt, "check-interval=i", "mibs-dirs=s",
           "ifindex=s") or usage();
defined $opt{ifindex} or usage();
Snabb::SNMP::Agent::start({ name => "pseudowire",
                            subtrees => \%subtrees,
			    check_interval => $opt{'check-interval'},
                            if_index => $opt{ifindex},
                            mibs_dirs => $opt{'mibs-dirs'},
                            mibs => 'CISCO-SMI:CISCO-IETF-PW-TC-MIB'
                            .':CISCO-IETF-PW-MIB:PW-STD-MIB'
                            .':CISCO-IETF-PW-ENET-MIB:PW-ENET-STD-MIB',
                            shmem_dir => $opt{'shmem-dir'},
			  });

## Not reached

## Local Variables:
## mode: CPerl
## End: