8 use FS::Record qw(qsearch);
10 # careful about importing anything here--it will end up in a LOT of
13 use vars qw(@subclasses $DEBUG $conf);
17 # any subclass that can have IP addresses needs to be added here
18 @subclasses = (qw(FS::svc_broadband FS::svc_acct));
21 $conf ||= FS::Conf->new;
26 FS::IP_Mixin - Mixin class for objects that have IP addresses assigned.
30 The inheritor may provide the following methods:
34 =item ip_addr [ ADDRESS ]
36 Get/set the IP address, as a string. If the inheritor is also an
37 L<FS::Record> subclass and has an 'ip_addr' field, that field will be
38 used. Otherwise an C<ip_addr> method must be defined.
40 =item addr_block [ BLOCK ]
42 Get/set the address block, as an L<FS::addr_block> object. By default,
43 the 'blocknum' field will be used.
45 =item router [ ROUTER ]
47 Get/set the router, as an L<FS::router> object. By default, the
48 'routernum' field will be used. This is strictly optional; if present
49 the IP address can be assigned from all those available on a router,
50 rather than in a specific block.
52 =item _used_addresses [ BLOCK ]
54 Return a list of all addresses in use (within BLOCK, if it's specified).
55 The inheritor should cache this if possible.
57 =item _is_used ADDRESS
59 Test a specific address for availability. Should return an empty string
60 if it's free, or else a description of who or what is using it.
70 The method that should be called from check() in the subclass. This does
73 - In an C<auto_router> situation, sets the router and block to match the
75 - Otherwise, if the router and IP address are both set, validate the
76 choice of router and set the block correctly.
77 - Otherwise, if the router is set, assign an address (in the selected
78 block if there is one).
79 - Check the IP address for availability.
81 Returns an error if this fails for some reason (an address can't be
82 assigned from the requested router/block, or the requested address is
83 unavailable, or doesn't seem to be an IP address).
85 If router and IP address are both empty, this will do nothing. The
86 object's check() method should decide whether to allow a null IP address.
93 if ( $self->ip_addr eq '0.0.0.0' ) { #ipv6?
97 # strip user-entered leading 0's from IPv4 addresses
98 # Parsers like NetAddr::IP interpret them as octal instead of decimal
102 split( /\./, $self->ip_addr )
104 ) if $self->ip_addr =~ /\./ && $self->ip_addr =~ /[\.^]0/;
108 and $self->conf->exists('auto_router') ) {
109 # assign a router that matches this IP address
110 return $self->check_ip_addr || $self->assign_router;
112 if ( my $router = $self->router ) {
113 if ( $router->manual_addr ) {
114 # Router is set, and it's set to manual addressing, so
115 # clear blocknum and don't tamper with ip_addr.
116 $self->addr_block(undef);
118 my $block = $self->addr_block;
119 if ( !$block or !$block->manual_flag ) {
120 my $error = $self->assign_ip_addr;
121 return $error if $error;
123 # otherwise block is set to manual addressing
126 return $self->check_ip_addr;
131 Set the IP address to a free address in the selected block (C<addr_block>)
132 or router (C<router>) for this object. A block or router MUST be selected.
133 If the object already has an IP address and it is in that block/router's
134 address space, it won't be changed.
142 #otherwise we'll get the same assignment for concurrent identical calls
143 # this will serialize them
144 $_->lock_table foreach @subclasses;
147 my $na = $self->NetAddr;
149 if ( $self->addr_block ) {
150 # choose an address in a specific block.
151 @blocks = ( $self->addr_block );
152 } elsif ( $self->router ) {
153 # choose an address from any block on a specific router.
154 @blocks = $self->router->auto_addr_block;
156 # what else should we do, search ALL blocks? that's crazy.
157 die "no block or router specified for assign_ip_addr\n";
162 foreach my $block (@blocks) {
163 if ( $self->ip_addr and $block->NetAddr->contains($na) ) {
166 # don't exit early on assigning a free address--check the rest of
167 # the blocks to see if the current address is in one of them.
169 $new_addr = $block->next_free_addr;
174 return 'No IP address available on this router' unless $new_addr;
176 $self->ip_addr($new_addr->addr);
177 $self->addr_block($new_block);
183 If the IP address is set, set the router and block accordingly. If there
184 is no block containing that address, returns an error.
190 return '' unless $self->ip_addr;
191 my $na = $self->NetAddr;
192 foreach my $router (qsearch('router', {})) {
193 foreach my $block ($router->addr_block) {
194 if ( $block->NetAddr->contains($na) ) {
195 $self->addr_block($block);
196 $self->router($router);
201 return $self->ip_addr . ' is not in an allowed block.';
206 Validate the IP address. Returns an empty string if it's correct and
207 available (or null), otherwise an error message.
213 my $addr = $self->ip_addr;
214 return '' if $addr eq '';
215 my $na = $self->NetAddr
216 or return "Can't parse address '$addr'";
217 # if there's a chosen address block, check that the address is in it
218 if ( my $block = $self->addr_block ) {
219 if ( !$block->NetAddr->contains($na) ) {
220 return "Address $addr not in block ".$block->cidr;
223 # if the address is in any designated ranges, check that they don't
225 foreach my $range (FS::addr_range->any_contains($addr)) {
226 if ( !$range->allow_use ) {
227 return "Address $addr is in ".$range->desc." range ".$range->as_string;
230 # check that nobody else is sitting on the address
231 # (this returns '' if the address is in use by $self)
232 if ( my $dup = $self->is_used($self->ip_addr) ) {
233 return "Address $addr in use by $dup";
243 if ( defined $new ) {
244 die "addr_block() must take an address block"
245 unless $new->isa('FS::addr_block');
246 $self->blocknum($new->blocknum);
254 # could cache this...
255 FS::addr_block->by_key($self->blocknum);
262 if ( defined $new ) {
263 die "router() must take a router"
264 unless $new->isa('FS::router');
265 $self->routernum($new->routernum);
269 $self->routernum('');
273 FS::router->by_key($self->routernum);
276 =item used_addresses [ BLOCK ]
278 Returns a list of all addresses (in BLOCK, or in all blocks)
279 that are in use. If called as an instance method, excludes
280 that instance from the search.
287 return ( map { $_->_used_addresses($block, $self) } @subclasses );
290 sub _used_addresses {
292 die "$class->_used_addresses not implemented";
295 =item is_used ADDRESS
297 Returns a string describing what object is using ADDRESS, or
298 an empty string if it's not in use.
306 my $used = $_->_is_used($addr, $self);
307 return $used if $used;
314 die "$class->_is_used not implemented";
321 We can't reliably check for duplicate addresses across tables. A
322 more robust implementation would be to put all assigned IP addresses
323 in a single table with a unique index. We do a best-effort check
324 anyway, but it has a race condition.