Wednesday, January 29, 2020

Linux serial console over AMT SOL - in practical way

One of hidden gem and underrated in AMT is Serial-Over-LAN (SOL). For most Unix veterans, embedded developers and Telco equipment/IT admins, managing devices using serial port is common rite. We occasionally connect serial port over modem for remote access. Sometimes we hook it to Serial over IP box so that it is reachable within enterprise.

AMT Serial-Over-LAN brings this communication port to the next level. With AMT, Serial-Over-LAN boundary now expands with the connection tunnel established by AMT (either local network or across the internet using CIRA).

For most AMT capable system or vPro certified platform, the chipset will expose a serial port which is managed by Manageability Engine. This device can be used by locally running software to communicate with remote party. The remote party can connect to this serial port using AMT SOL Redirection protocol.

So, how do you find out which serial port is AMT SOL in Linux?

A simple dmesg will list ttyS recognized by Linux kernel and from the above result AMT SOL port is ttyS1. Usually the IO address is quite high and it is connected to AMT and it usually shared the same location as HECI interface.

See the output of lspci below.

Now we know that ttyS1 is AMT SOL, how can we activate serial console access on that?

Let start with a systemd based system like Centos (Note: this step also applies to Ubuntu too) here is the command:
systemctl enable serial-getty@ttyS1.service
systemctl start serial-getty@ttyS1.service

To check if the service is running, use:
systemctl status serial-getty@ttyS1.service

The typical output of this is like this:

Ok, so it is running and listening to ttyS1. So let see it in action.

First of all, I have this machine connected to MeshCentral with CIRA.


Next, let;s flip to the Terminal tab, click HW Connect (this means connect to AMT SOL) and press Enter.

A TTY login prompt should be displayed and you should be able to login using your credential.


Now you can access the shell from Meshcentral.com. This feature will still work although MeshAgent is not running or if OS network stack is not working. All AMT SOL traffic runs through AMT. A very convenient and efficient feature to troubleshoot issue before costly sending technician to fix onsite.

Tuesday, January 21, 2020

mc2router - a simple yet versatile tool to get stuff done over Meshcentral 2

Meshcentral 2 (or we passionately call it MC2) has been remote management choice for many of my team members for more than 3 years. Being able to remotely see the screen, access the shell and transfer files to our devices regardless the location of the devices have been very valuable to get stuff done.

While contributing to this project, I realized that there is a very nifty feature of MC2 which is network tunneling features. Meshcentral 2 comes with small router application however to get stuff done quickly I thought it would be great if there is a simple Node Webkit or Electron-based application to simplify this. So, I wrote this with feedback and contributions from many friends.



Unassumingly, it looks like a simple little utility and it has the same theme as MC2. Well, the creator of MC2 himself style it. Thanks Ylian.

Once logged in, you should be able to see all the meshes (the terminology recently has changed to device groups) and the devices on each mesh.

Let say we select one of the machine and then click SSH. A Putty shell will pop up and it connects to local listening socket (with randomly assigned port) fired up by mc2router. It will then trigger establishing network tunnel to the agent of that device to particular TCP port (in this case port 22). Since this is SSH, obviously SSH negotiation will start and if we do not have the device certificate fingerprint saved then Putty certificate warning will pop up.


Once the certificate is accepted, we should see normal Putty login prompt. Fill in the credential and if successful we should see shell like normal SSH session.

Besides SSH, this utility has RDP, FileZilla/SFTP capabilities. If users would like to custom tunnel their favorite application, there is custom button to configure (see File->Configure). Oh, by the way this utilities supports HTTP and SOCKS proxy too.

If you would like to play around with this utility, please see https://github.com/jsastriawan/mc2router. Feedback via Github issue is welcome.

Monday, September 9, 2013

NMEA string parser in Python - if you want to develop GPS tracker software in Python

GPS devices usually spit out location information in NMEA format. This format is quite standard. Here is the example of NMEA strings:

$GPGGA,002153.000,3342.6618,N,11751.3858,W,1,10,1.2,27.0,M,-34.2,M,,0000*5E
$GPRMC,161229.487,A,3723.2475,N,12158.3416,W,0.13,309.62,120598, ,*10

Those two lines contains geo-location coordinates, bearing, speed and also some information about the number of satellites being used.

I wrote this simple utilities to parse out those strings to kill time and hopefully this little piece of code inspires other to work on geo-location related services.

# NMEA messages decoding library
# Main decoder of the string messages
def nmea_decode(str):
ret = dict({'type': 'unknown'});
data = str.split(',');
if (len(data)>0):
# start decoding messages
if (data[0] == '$GPGGA'):
ret['type']='GGA';
ret.update(decode_gga(data));
elif (data[0] == '$GPRMC'):
ret['type']='RMC';
ret.update(decode_rmc(data));
#elif (data[0] == '$GPGSA'):
#print(data[0]+' not decoded')
#elif (data[0] == '$GPGSV'):
#print(data[0]+' not decoded')
# fall back
return ret;

def decode_gga(da):
ret = dict()
if (da[0]=='$GPGGA' and len(da)==15):
ret['utc_time']= da[1][0:2]+':'+da[1][2:4]+':'+da[1][4:]
ret['lat_deg']=int(da[2][0:2])+(float(da[2][2:])/60)
ret['lat_dm']=da[2][0:2]+' '+da[2][2:]
ret['ns']=da[3]
ret['lon_deg']=int(da[4][0:3])+(float(da[4][3:])/60)
ret['lon_dm']=da[4][0:3]+' '+da[4][3:]
ret['ew']=da[5]
ret['pfi']=da[6]
ret['sat_used']=da[7]
ret['hdop']=da[8]
ret['msl_alt']=da[9]
ret['alt_unit']=da[10]
ret['geoid_sep']=da[11]
ret['sep_unit']=da[12]
ret['age_diff_cor']=da[13]
ret['diff_ref_sta_id']=da[14][0:4]
ret['csum']=da[14][4:]
return ret;

def decode_rmc(da):
ret = dict()
if (da[0]=='$GPRMC' and len(da)==12):
ret['utc_time']= da[1][0:2]+':'+da[1][2:4]+':'+da[1][4:]
ret['status']=da[2]
ret['lat_deg']=int(da[3][0:2])+(float(da[3][2:])/60)
ret['lat_dm']=da[3][0:2]+' '+da[3][2:]
ret['ns']=da[4]
ret['lon_deg']=int(da[5][0:3])+(float(da[5][3:])/60)
ret['lon_dm']=da[5][0:3]+' '+da[5][3:]
ret['ew']=da[6]
ret['speed_knot']=da[7]
ret['speed_km']=float(da[7])*1.852
ret['course']=da[8]
ret['date']=da[9][4:]+'-'+da[9][2:4]+'-'+da[9][0:2]
ret['mode']=da[10]
ret['csum']=da[11]
return ret;