Mikrotik API with PHP

Hi,

I have little problem with my php script.

Here is my php script:

<?php
require('config.php');
require('routeros_api.class.php');

$API = new routeros_api();
$API->debug = true;
$dediny = array(
                array("10.30.1."),
                array("10.30.2."),
                array("10.30.31."),
);
$ip = array(
            array("10.30.1.1", "Peter123", "xxx"),
            array("10.30.2.1", "Peter123", "xxx"),
            array("10.30.31.1", "Peter123", "xxx"),  
); 

foreach($ip as $mikrotik){
  foreach($dediny as $dedina){
   if ($API->connect($mikrotik[0], $mikrotik[1], $mikrotik[2])) {
      $query = mysql_query("SELECT usermac, userip, username FROM sl_internet WHERE userip like '.$dedina[0].%' and usermac IS NOT NULL");
      while($row = mysql_fetch_array($query)){  
        $usermac  = $row['usermac'];
        $username = $row['username'];
        $uzivatel = strtr($username, ' ./áäčďéěëíµňôóöŕřšťúůüýžÁÄČĎÉĚËÍĄŇÓÖÔŘŔŠŤÚŮÜÝŽ', '___aacdeeeilnooorrstuuuyzaacdeeelinooorrstuuuyz');  
        $API->write("/interface/wireless/access-list/add", false);
        $API->write("=forwarding=no", false);
        $API->write("=authentication=yes", false);
        $API->write("=interface=all", false);
        $API->write("=mac-address=". $usermac ."", false);
        $API->write("=comment=". $uzivatel ."");
  
        $READ = $API->read(false);
        $ARRAY = $API->parse_response($READ);

        print_r($ARRAY);

        $API->disconnect();
      } 
    }
  }
}   
   
?>

My problem is > user is login to mikrotik, but dont write mac address to address list, where is problem?

Here is message after I run script:

Pokus c. #1 to 10.30.1.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=32a065fe9daba77f3c5131fe9294b93e <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=001474e0d360b775f9f923580ea95b6531 >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.1.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=7206547048eec1e0534e37022bb0bfde <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=00a340b63c0a50704f74e02f67a37dbfbd >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.1.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=44c48f9f023aca98e8ded349e2f1170b <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=002c5d6607a10717b1451a29e364d3631c >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.2.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=1a3eea89741b8af595f5cdd05ad9bdb8 <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=00b7ed1b0084a8444e5a05fc6740907a94 >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.2.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=90012ab87e8fdc28939546dd09d060e2 <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=00ffd2da3ec2cc85f75b61bd9814f2d654 >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.2.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=027a28f68da888643ea623c12f7f7b1a <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=00088dc34835b6b70c767ce468bdde5a39 >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.31.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=a9da030586223ef96a396d27e6dbcd3c <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=00891ba7cc96c3d64c45020fb015f2f88e >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny... Pokus c. #1 to 10.30.31.1:8728... Chyba... Pokus c. #1 to 10.30.31.1:8728... <<< [6] /login >>> [5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37, 1]=ret=c5d47c20ed608b3c88512315972621bc <<< [6] /login <<< [14] =name=Peter123 <<< [44] =response=000c3912efd857b8ff542e3f86c69698ef >>> [5/5] bytes read. >>> [5, 1]!done Pripojeny...

What’s the

Pripojeny… Pokus c. #1 to 10.30.1.1:8728

part? Doesn’t look like it should be part of the output… unless you’re using a very different code from the one you’re showing.

Also, I think you might want to use mysql_fetch_assoc() instead of mysql_fetch_array(), as with your current code, you might be ending up with empty strings if PHP has decided to use a numeric array.

I have MySQL database, where is table of user who have mac address and IP address.

I have many mikrotik in some village.

Exemple: User Peter have IP address 10.30.3.2 and mac address 00:00:00:00:00
I must send user mac address to address list on mikrotik 10.30.3.1 (gateway).

But problem is, that I have many users who have different ip address.

I select from mysql ip address users who start on ip 10.30.3. and send it on mikrotik 10.30.3.1

I kind of infered that from your code, but it still seems like something is not the same as what you’re showing.

Here’s an alternative version, using the client from my signature:

<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2/Net/RouterOS/Autoload.php';
require_once 'config.php';

$networks = array(
    array("10.30.1."),
    array("10.30.2."),
    array("10.30.31.")
);
$routers = array(
    array("10.30.1.1", "Peter123", "xxx"),
    array("10.30.2.1", "Peter123", "xxx"),
    array("10.30.31.1", "Peter123", "xxx") 
);
try {
    foreach ($routers as $router) {
        $client = new Client($router[0], $router[1], $router[2]);
        $client->setCharset(array(
            Communicator::CHARSET_REMOTE => 'ASCII',
            Communicator::CHARSET_LOCAL => 'UTF-8'
        ));
        foreach ($networks as $network) {
            $query = "SELECT usermac, userip, username
            FROM sl_internet
            WHERE userip LIKE '{$network[0]}%' AND usermac IS NOT NULL";
            $result = mysql_query($query);
            if (false === $result) {
                throw new \Exception(
                    "Query failed with message '" . mysql_error() .
                    "'. The query is \"{$query}\"", mysql_errno()
                );
            }
            while ($row = mysql_fetch_assoc($result)) {
                $addRequest = new Request(
                    '/interface wireless access-list add forwarding=no authentication=yes interface=all'
                );
                $responses = $client->sendSync(
                    $addRequest
                        ->setArgument('mac-address', $row['usermac'])
                        ->setArgument('comment', $row['username'])
                );
                foreach ($responses->getAllOfType(Response::TYPE_ERROR) as $error) {
                    echo "Error:\n";
                    foreach ($error->getAllArguments() as $name => $value) {
                        echo "{$name}:{$value}\n";
                    }
                    echo "\n";
                }
            }
        }
    }
} catch(\Exception $e) {
    echo $e;
} 

Upon making it, I also found what I believe is the cause of your problems. In your SQL query, you have

like '.$dedina[0].%'

which for “10.30.1.” will be interpolated as

like '.10.30.1..%'

and since your networks don’t start with a dot, nor contain a “..”, your query returns an empty result set, so nothing ever gets added. I’ve corrected that in the sample above.

Thanks! But I was edited my script:

<?php
require('config.php');
require('routeros_api.class.php');

$API = new routeros_api();
$API->debug = true;
$dediny = array(
                array("10.30.1."),
                array("10.30.2."),
                array("10.30.31."),
);
$ip = array(
            array("10.30.1.1", "Peter123", "xxx"),
            array("10.30.2.1", "Peter123", "xxx"),
            array("10.30.31.1", "Peter123", "xxx"),  
); 

foreach($ip as $mikrotik){
  foreach($dediny as $dedina){
   if ($API->connect($mikrotik[0], $mikrotik[1], $mikrotik[2])) {
      $query = mysql_query("SELECT usermac, userip, username FROM sl_internet WHERE userip LIKE '$dedina[0]%' and usermac IS NOT NULL");
      while($row = mysql_fetch_array($query)){  
        $usermac  = $row['usermac'];
        $username = $row['username'];
        $uzivatel = strtr($username, ' ./áäèïéìëíµòôóöàøšúùüýžÁÄÈÏÉÌËÍ¥ÒÓÖÔØÀŠÚÙÜÝŽ', '___aacdeeeilnooorrstuuuyzaacdeeelinooorrstuuuyz');  
        $API->write('/interface/wireless/access-list/add', false);
        $API->write('=forwarding=no', false);
        $API->write('=authentication=yes', false);
        $API->write('=interface=all', false);
        $API->write("=mac-address=$usermac", false);
        $API->write("=comment=$uzivatel");
  
        $READ = $API->read(false);
        $ARRAY = $API->parse_response($READ);

        print_r($ARRAY);

        
      } 
    }
  }
} 
  
$API->disconnect();   
?>

Now script write all user mac to all mikrotik :frowning:

Wait… wasn’t that what you wanted?

Oh… you want each network in the according router, I see.

The easiest way is to have the network itself as part of the data about the router… like as a key for example.

Here’s an adjusted version of my code above (tweak it accordingly for yours):

<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2/Net/RouterOS/Autoload.php';
require_once 'config.php';

$routers = array(
    "10.30.1." => array("10.30.1.1", "Peter123", "xxx"),
    "10.30.2." => array("10.30.2.1", "Peter123", "xxx"),
    "10.30.31." => array("10.30.31.1", "Peter123", "xxx") 
);
try {
    foreach ($routers as $network => $router) {
        $client = new Client($router[0], $router[1], $router[2]);
        $client->setCharset(
            array(
                Communicator::CHARSET_REMOTE => 'ASCII',
                Communicator::CHARSET_LOCAL => 'UTF-8'
            )
        );
        $query = "SELECT usermac, userip, username
        FROM sl_internet
        WHERE userip LIKE '{$network}%' AND usermac IS NOT NULL";
        $result = mysql_query($query);
        if (false === $result) {
            throw new \Exception(
                "Query failed with message '" . mysql_error() .
                "'. The query is \"{$query}\"", mysql_errno()
            );
        }
        while ($row = mysql_fetch_assoc($result)) {
            $addRequest = new Request(
                '/interface wireless access-list add forwarding=no authentication=yes interface=all'
            );
            $responses = $client->sendSync(
                $addRequest
                    ->setArgument('mac-address', $row['usermac'])
                    ->setArgument('comment', $row['username'])
            );
            foreach ($responses->getAllOfType(Response::TYPE_ERROR) as $error) {
                echo "Error:\n";
                foreach ($error->getAllArguments() as $name => $value) {
                    echo "{$name}:{$value}\n";
                }
                echo "\n";
            }
        }
    }
} catch(\Exception $e) {
    echo $e;
} 

OK THANKY YOU VERY MUCH! :slight_smile:

But I solved it with my php script. I have one problem now.

How I can check if mac address exists? If exists do nothing, if dont exists write mac address.

Do another print request with a query for that, and check if it returns any replies.

In my client

if (null === $client->sendSync(new Request('/interface wireless access-list print .proplist=.id', null, Query::where('mac-address', $row['usermac'])))->getArgument('.id')) {
//No entry with such a MAC address. Add it here.
} 

Thank you, but i dont use your php api class. What is command for write?

'?mac-address=' . $row['usermac'] 

(as a part of the print comm() call)

Buen dia.


Estoy tratando de trabajar con la clase php para mikrotik, pero no he
logrado hacerlo trabajar en el servicio de hosting contratado
(Godaddy), pero desde mi laptop (con Appserv) conectado a la mikrotik sí funciona.
Agradezco su colaboración. Tengo una Routerboard RB1100.



Desde el hosting:



Connection attempt #1 to telmovil.4dq.com:8728… Connection attempt
#2 to telmovil.4dq.com:8728… Connection attempt #3 to telmovil.4dq.com:8728
Connection attempt #4 to telmovil.4dq.com:8728… Connection attempt
#5 to telmovil.4dq.com:8728… Error…



Desde mi laptop, con Appserv:



Connection attempt #1 to telmovil.4dq.com:8728… <<< [6] /login >>>
[5/5] bytes read. >>> [5, 39]!done >>> [37/37] bytes read. >>> [37,
1]=ret=bfba4d9674433881af0c85abe446e063 <<< [6] /login <<< [11]
=name=xxxxx <<< [44] =response=006245a8313d081bd97f2679c2ab870e53 >>> [5/5] bytes read.

[5, 1]!done Connected… <<< [18] /ip/route/rule/add <<< [22]
=src-address=10.1.0.99 <<< [19] =action=unreachable <<< [27]
=comment=adicionado por api >>> [5/5] bytes read. >>> [5, 10]!done >>>
[8/8] bytes read. >>> [8, 1]=ret=*49 <<< [59] /ip route rule add
src-address=10.1.0.99 action=unreachable Disconnected…

Gracias.

@juanmesa

Please use English. I can’t understand… Spanish (I assume).


Anyway, looking at the log, I think I know what your problem is. I’m guessing in your code you have something like:

$API->comm('/ip route rule add src-address=10.1.0.99 action=unreachable'); 

or maybe

$API->write('/ip route rule add src-address=10.1.0.99 action=unreachable', false); 

Either way, that is not allowed with Denis’ class. Looking at the log, it also seems that earlier in your code, you’re actually doing it correctly. Remove any part in your code that looks like the above examples, and use whatever that other one that worked.

Hi boen_robot,

I have one problem with Mikrotik address list.
mikrotik.JPG
We have three AP, with three ip address in one mikrotik.

Everyone AP have ip address.
AP 1 - 10.30.31.1
AP 2 - 10.30.32.1
AP 3 - 10.30.33.1

We have MySQL database where are ip address and mac address our clients.

How I can create php script, if I mac address:

  • exists in mysql and mikrotik → do nothing
  • exists in mysql and not exists in mikrotik → write mac address to address list
  • not exists in mysql and exists in mikrotik → delete mac address from address list

Thank You!

@P3T3R

There are a few ways to go about this, depending on where you want to put the most burden - the router, the web server or the database server? Is memory an issue anywhere? How does the DB table look like? Are we talking about the ARP list anyway?

I’ll assume memory isn’t a big issue on the web server (although it’s still very limited, at least artificially with php.ini), and that network connections to the router are more expensive than connections to the DB server, as is typically the case (especially if the DB server runs on the same machine as the web server). I’m also assuming the following DB table structure:

CREATE TABLE `arp` (
  `ip` varchar(15) NOT NULL,
  `mac` char(17) NOT NULL,
  PRIMARY KEY (`ip`,`mac`)
) ENGINE=InnoDB

First, make a single print request to the router, so that you get all entries in it. Then, for each entry in the list, check if it exists in the DB by simply making a query for it, and checking if there’s a result row. If it doesn’t exist, add the entry’s ID to a removal list. If it does exist, add the IP/MAC pair to a string that would later form an SQL query. After the entries are processed, make a single remove request to the router with all IDs collected, and make an SQL query that tells you all entries other than the ones previously collected, and make an add request for each.

If you don’t mind, I’ll use my client for illustration purposes, but you can rewrite it in Denis’ class if you must:

<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2/Net/RouterOS/Autoload.php';

//Connect to both the router and MySQL.
$client = new Client('10.30.99.31', 'admin', 'password');
$mysqli = new \mysqli('127.0.0.1', 'root', '', 'db');

//For performance's sake, use prepared statements.
$selectStatement = array(
    'stmt' => $mysqli->prepare('SELECT `ip`, `mac` FROM `arp` WHERE `ip`= ? AND `mac` = ?'),
    'params' => array('ip' => null, 'mac' => null)
);
$selectStatement->bind_param('ss', $selectStatement['params']['ip'], $selectStatement['params']['mac']);

//Variables for use later.
$removalIDs = '';
$finalSelectQueryFilters = array();

//Make a request that will get info about all ARP entries... without the comments.
$printRequest = new Request('/ip arp print .proplist=.id,address,mac-address');
$printRequest->setTag('p');

//Define what to do on each entry. The approach below ensures minimal memory consumption for this phase.
$client->sendAsync($printRequest,
function ($response) use ($mysqli, &$selectStatement, &$removalIDs, &$finalSelectQueryFilters) {
    if ($response->getType() === Response::TYPE_DATA) {
        $selectStatement['params']['ip'] = $response->getArgument('address');
        $selectStatement['params']['mac'] = $response->getArgument('mac-address');
        $selectStatement['stmt']->execute();
        if ($selectStatement['stmt']->get_result()->num_rows) {
            $finalSelectQueryFilters[] =
                "(`ip` = '" . $mysqli->real_escape_string($response->getArgument('address')) .
                "' AND `mac` = '" . $mysqli->real_escape_string($response->getArgument('mac-address')) .
            "')";
        } else {
            $removalIDs .= $response->getArgument('.id') . ',';
        }
    }
});
$client->loop();
//$removalIDs and $finalSelectQueryFilters are now filled. Now let's make use of them.

//Remove all entries not in the DB.
$removeRequest = new Request('/ip arp remove');
$removeRequest->setArgument('numbers', rtrim($removalIDs, ','));
$client->sendSync($removeRequest);

//Get all entries that are in the DB, but not in the router
$finalSelectResult = $mysqli->query('SELECT `ip`, `mac` FROM `arp` WHERE NOT (' . implode(' OR ', $finalSelectQueryFilters) . ')');

//Add all such entries to the routers.
$addRequest = new Request('/ip arp add');
for ($i=0; $row = $finalSelectResult->fetch_assoc(); ++$i) {
    $client->sendAsync(
        $addRequest
            ->setArgument('address', $row['ip'])
            ->setArgument('mac-address', $row['mac'])
            ->setTag($i)
    );
}
$client->loop(); 

BTW, things would be significantly easier if you can simply add the entries to the router at the time you add them to the DB, and remove them from the router when you remove them from the DB.

Hi boen_robot,

thank you for you reply, but if I have more $client-s? We have web server + mysql server on one server, where is about 2000 clients IP and we have about 200 mikrotik IPs.

Your script is for ARP list, can You write me script for Wireless - access list?

Thank You very much!

For the fact that there are multiple routers, you have an example at my second post - basically, just create a new Client object for each router.

For the access-list… sorry, I didn’t previously noticed you wanted to deal with just the macs (I got distracted by the fact your DB contains the IPs as well, so ARP was my first thought). Does the database also contain the router in which the user is found? Is this data different on per-router basis, or can any user connect from any router?

I’ll assume it is different between routers (with usernames being allowed to repeat, as long as they’re on different routers), and that the DB does keep track of this using a separate table with router credentials. So a schema with something like:

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

-- -----------------------------------------------------
-- Table `routers`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `routers` (
  `routerID` INT(11) NOT NULL AUTO_INCREMENT ,
  `ip` VARCHAR(15) NOT NULL ,
  `username` VARCHAR(255) NOT NULL ,
  `password` VARCHAR(255) NOT NULL ,
  PRIMARY KEY (`routerID`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

SHOW WARNINGS;

-- -----------------------------------------------------
-- Table `clients`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `clients` (
  `ip` VARCHAR(15) NOT NULL ,
  `mac` CHAR(17) NOT NULL ,
  `routerID` INT(11) NOT NULL ,
  `username` VARCHAR(255) NULL DEFAULT NULL ,
  PRIMARY KEY (`ip`, `mac`, `routerID`) ,
  INDEX `fk_clients_routers` (`routerID` ASC) ,
  CONSTRAINT `fk_clients_routers`
    FOREIGN KEY (`routerID` )
    REFERENCES `router`.`routers` (`routerID` )
    ON DELETE NO ACTION
    ON UPDATE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

SHOW WARNINGS;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

Now, because the code organization is about to go berzerk, I’ve abstracted away a lot of the previous code into its own class. I suppose I could’ve done it better, but at this point, it’s simply about cleaning up what was previously an anonymous function. Most of the other code is pretty much the same, minus all the IP related stuff:

<?php
namespace PEAR2\Net\RouterOS;
require_once 'PEAR2/Net/RouterOS/Autoload.php';

//Connect to MySQL.
$mysqli = new \mysqli('127.0.0.1', 'root', '', 'db');

class ResponseProcessor {
    protected $mysqli;
    protected $stmt;
    protected $params = array('routerID' => null, 'mac' => null);
    protected $removalIDs = '';
    protected $existingMACs = '';
    
    public function __construct(\mysqli $mysqli) {
        $this->mysqli = $mysqli;
        $this->stmt = $mysqli->prepare('SELECT `mac` FROM `clients` WHERE `routerID` = ? AND `mac` = ?');
        $this->stmt->bind_param('is', $this->params['routerID'], $this->params['mac']);
    }
    
    public function setRouterID($id) {
        $this->params['routerID'] = (int) $id;
        $this->removalIDs = $this->existingMACs = '';
    }
    
    public function process(Response $response) {
        if ($response->getType() === Response::TYPE_DATA) {
            $this->params['mac'] = $response->getArgument('mac-address');
            $this->stmt->execute();
            if ($this->stmt->get_result()->num_rows) {
                $this->existingMACs .=
                    "'" .
                    $this->mysqli->real_escape_string($response->getArgument('mac-address'))
                    . "',";
            } else {
                $this->removalIDs .= $response->getArgument('.id') . ',';
            }
        }
    }
    
    public function getRemovalIDs() {
        return rtrim($this->removalIDs, ',');
    }
    
    public function getAllNotInRouter() {
        return $this->mysqli->query('SELECT `mac` FROM `clients` WHERE `routerID` = ' . $this->params['routerID'] . ' AND `mac` NOT IN (' . rtrim($this->existingMACs, ',') . ')');
    }
}

$printRequest = new Request('/interface wireless access-list print .proplist=.id,mac-address');
$printRequest->setTag('p');

$removeRequest = new Request('/interface wireless access-list remove');
$addRequest = new Request('/interface wireless access-list add');

$processor = new ResponseProcessor($mysqli);
$routers = $mysqli->query('SELECT `routerID`, `ip`, `username`, `password` FROM `routers`');
while ($router = $routers->fetch_assoc()) {
    $client = new Client($router['ip'], $router['username'], $router['password']);
    $processor->setRouterID($router['routerID']);

    //Define what to do on each entry. The approach below ensures minimal memory consumption for this phase.
    $client->sendAsync($printRequest, array($processor, 'process'));
    $client->loop();

    //Remove all entries not in the DB.
    $client->sendSync($removeRequest->setArgument('numbers', $processor->getRemovalIDs()));

    //Get all entries that are in the DB, but not in the router
    $finalSelectResult = $processor->getAllNotInRouter();

    //Add all such entries to the routers.
    for ($i=0; $row = $finalSelectResult->fetch_assoc(); ++$i) {
        $client->sendAsync(
            $addRequest
                ->setArgument('mac-address', $row['mac'])
                ->setTag($i)
        );
    }
    $client->loop();
} 

You’ll notice at the end that each MAC address is added on “all” wireless interfaces, rather than a specific one. If you wish to also assign the proper interface, you’ll have to either store the interface in the DB alongside the MAC address or use something like PEAR’s Net_IPv4::ipInNetwork() function to automatically determine the interface based on the user’s IP and the AP’s network addresses on the current router. The first option costs space at the DB server (obviously; 2000 users, times at least 5 bytes per user = at least 10000 bytes or 10kb, with this growing quickly depending on how long your interface names are), and the second one costs some time (since each entry-to-be-added will have to be checked, and before that, you’ll have to make one extra request to get all the APs network addresses; we’re talking about something around a few miliseconds of average overhead, half a second at worst, assuming no network problems and decent number of entries-to-be-added). What would you like sacrificed?

ok. I’ll try do it in english.

The api connect fine from my laptop in the lan. i Wish execute the code from my website in godaddy, but i receive:

Connection attempt #1 to telmovil.4dq.com:8728… Connection attempt
#2 to telmovil.4dq.com:8728… Connection attempt #3 to telmovil.4dq.com:8728
Connection attempt #4 to telmovil.4dq.com:8728… Connection attempt
#5 to telmovil.4dq.com:8728… Error…

It doesn’t connect.

Thanks for your interest and help.