I2C Man-in-the-Middle

Go To Last Post
21 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

I'm using an XMEGA A3BU dev board, which has 2 hardware I2C interfaces. I'm trying to achieve a full, bi-directional man-in-the-middle "attack" between an I2C Master and an I2C Slave device. I tried using ASF, but I quickly realised it was using blocking operations, and it left me little to no time to send the received packets through. So I rolled my own implementation, which just checks if any of the flags are set, and acts appropriately. This approach works perfectly on normal "write" operations. I receive the data on my slave side, then store it in a buffer. My master side notices that there's data to send, and it sends it through. This is relatively easy.

 

My main issue is with "read" operations. The external master device wants to access some data from the external slave device, but my board is sitting in the middle. The "read" request hits my board's slave, which doesn't have the data to pass back. So it signals the on-board master to get 1 byte of data from the external slave. When it gets it, it cannot ack or nack the transaction, because we don't know if the external master would respond with an ack or nack to that single byte. So now the internal slave sends the byte to the external master, and waits for an ack or nack. Once that's done, the internal master can finally ack or nack the external slave. This goes on until the transaction is complete. To my understanding, this approach requires lots of signalling to tell which side has to do what, and whether it's done it already, or not. It got to a level of complexity, where my little brain can no longer reliably tell what's going on. I'm not using any interrupts. Am I going down the right route, or just completely missing something? Does this really require lots of signalling between the two sides to achieve this, or is there an easier approach?

 

Thank you very much for all the answers in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First, Welcome to AVRFreaks!

 

Wouldn't an I2C protocol analyzer be easier to use and to capture the data?

 

What is the purpose of this man-in-the-middle sniffer?

 

Jim

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ki0bk wrote:

Wouldn't an I2C protocol analyzer be easier to use and to capture the data?

 

What is the purpose of this man-in-the-middle sniffer?

 

Hi Jim,

 

Thank you for your reply.

 

This board I'm working on will have to sit between two devices to overwrite some of the packets and pass through the rest. The slave device is a camera that can receive settings, such as ISO, shutter speed values, and many more. In our use case, we want to use our own ISO and shutter speed values at all times, and leave all the other settings unchanged. The master is just a device that controls the camera via I2C and processes its footage. It occasionally reads from the slave as well. Using an analyzer wouldn't really help in this case, as this board wouldn't serve just a one-off purpose, even though I could pretty much turn it into an I2C protocol analyzer at its finished state.

 

All the best,

 

Tibor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In that case, I think I would try to emulate the camera, by reading all settings and registers, then interact with the master as if I were the camera on one I2C bus...

When the master makes a change to settings/registers, process them and act as the mater on another bus to the slave camera and update the needed changes,

rather then act as a man-in-the middle of one bus.  Your middle board, then keeps a copy of the camera data so read/writes takes place as needed.

I'm assuming with the above that there is no video data on the i2c bus, but only camera settings and commands.

Ignore if that is not the case...

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's correct, there is no video data on that bus. When I said "sit between two devices", what I really meant was I'm using 2 separate I2C buses, because the XMEGA A3BU has 2 hardware TWI interfaces. One is dedicated to communicating with the camera, and the other one is for communicating with the camera controller. Write operations pass through perfectly, because those are essentially unidirectional transactions, which I can easily replicate on the other bus. The issue is with reading, because one side expects data / or acknowledgement from the other side, and I seem to have to implement a complicated signalling system to tell which side has to do something, and what that action is. Normal I2C communication doesn't need this, because everything is based on the status of interrupt flags, but in this case I'm introducing new flags into this setup, which makes it really difficult to follow. I just thought someone might have had a similar requirement before, and I was wondering if my concept was actually viable.

 

All the best,

 

Tibor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have done what you are doing before, and what I did was just add the microcontroller in "parallel" (as a slave) with the bus, read all traffic, recognized packets that needed correction, waited for the original packet(s) to go through, and then sent my own packet to replace the original register value. In my case letting the original write go through did not cause an issue, and the traffic pattern was predictable, so I knew I had a long enough window of time after the original packet went through where the master wasn't sending anything, so I didn't deal with any collisions (writing at the same time as the legitimate master on the bus).

 

The only reason i did that was that I was using a very tiny microcontroller with only one USI on it and no spare pins or RAM. Otherwise acting as an inline repeater as you are doing will work too, but as you noted you will likely need to bitbang the SPI on both sides, because you will need to provide START, ACK, and NACK signals manually. I'm not sure if the Atmel TWI controller allows you to do this.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

First tool you need is of course a Logic Analyser. Follow my signature for a EUR 7 version.

 

2nd tool you need are devices that are capable of "Multi Master Mode". 

Having multiple masters on an I2C bus is oficcialy allowed and specified, but those pars are usually ignored (and therefore buggy) in software stacks etc.

"Multi Master Mode" is also optional, as it is not needen when there is only one master on the I2C bus.

 

Also Lots of small Linux computers such as the BeagleboneBlack, Cubie boards, Odroid's (from Hardkernel) and the Orange Pie's, all have hardware for I2C, and you can play with it directly from the command line with "I2C tools" (But it's probably pretty limited).

 

Another path you may want to go is to dive into "Micro Python". It's basically an Python command interpreter (REPL), You can log into it, Either via RS232 (or sometimes over WiFi and a web browser) and then type in python code which gets executed.

 

Have to confess:

I did not read all posts, just did a text search for "Logic analyser" and for "multi", and neither were mentioned before.

Have fun.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Take a look at "clock stretching" on the I2C bus.  Basically you in the middle will hold the bus lines low at the correct time while the master is sending a read request.  This will block the master.  You then pass the read request on to the slave, get the slave response, release the hold on the master, and pass the data back to the master (unmodified or modified as your application requires).

 

 

Last Edited: Thu. Apr 25, 2019 - 04:48 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi Paulvdh,

 

Unfortunately, your answer is not really relevant for my use case. As I mentioned before, I'm not looking for a packet sniffer for a one-off purpose. I am making a board that will be able to sit between an I2C slave and master by cutting their buses into two, completely separate buses. The atxmega256a3bu chip is capable of doing this. I'm already handling acking and nacking manually, and I have smart mode turned off, so I have full control over the buses without having to bit bang them. A multi-master setup wouldn't work, because I tested it, and the external master device is not prepared to handle multi-master mode, and loses arbitration completely, never recovering. I have to restart it to get it operating again. Unfortunately, time between transactions is also random and unpredictable.

 

I apologise, it's really hard to explain what my problem is. I don't have any issues with reading or writing using the I2C protocol. My problem is syncing up these 2 buses, so an action on one bus would indicate the other bus to act accordingly. When a master on bus-1 wants to read data, the master on bus-2 has to get hold of that data first, without actually answering with an ack or nack, because we don't know if the master on bus-1 would answer with an ack or nack. Once bus-1's master acked or nacked, we can do the same on bus-2. So it seems like I have to create lots of flags to indicate which side has to do what. That's on top of all the existing interrupt flags both sides have. I think I'll just go ahead and do that, and I'll create a C library to help out anyone who wants to achieve such a thing in the future.

 

 

All the best,

 

Tibor

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And clock stretching can do just that - hold up one side while you go talk to the other side

 

Clock stretching is part of I2C's protocol. It's purpose is for the slave to throttle the master when it needs more time to respond.  Sort of flow-control like.

 

In your case use this throttling mechanism to pause bus 1 while servicing bus 2.  Have the man-in-the-middle hold SCL low on bus 1, go talk to bus 2, then release bus SCL on bus 1 to continue the transaction.

 

https://www.i2c-bus.org/i2c-primer/clock-generation-stretching-arbitration/

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for not reading too much of the thread the first time, but your last post #9 paints clear picture. (Even though I'm not 100% sure it's the right picture).

And you were right the fist time. It looks like a man in the middle.

 

From what I understand you have no I2C problems at all, but only a problem with managing data in the uC to handle 2 separate I2C busses.

On the slave side, your uC must always be ready to react to a master on the other side of the bus.

If it is master on any of the 2 sides, it can decide for itself when it starts a transaction.

 

What you need are 2 interrupt driven I2C software stacks (or copies of the same (2 instances of a C++ class) and combine that with a bit of smart buffering.

2 Circular buffers are probably what you need, maybe the software is simpler if you have more circular buffers.

 

And don't say too lightly you don't need a Logic Analyser. It can for example easily measure latency between receiving a message on a I2C bus and transmission on the other.

That latency between the I2C busses is probably one of the most important criterea to design around, and can be tricky, especially if the I2C busses work at different baudrates. Clock stretching can be usefull, but buffering multiple bytes before you start a transmission may be a better Idea.

 

I hope you're familar with circular buffers or more general techniques for writing properly ISR's and efficient buffers (This is above "arduino" level programming).

If you're not familiar with circular buffers, then first study them and familiarize yourself with them before continuing with the next step.

 

The next step is a bit of high level design. And this is still better done with pen and paper than with a PC.

Make a few graphical drawings of the data flows, and how you can separate and compartamentalise them.

Combine that with a bit of simple pseudo code, and check it with your graphical overview if it has a good chance of working.

Don't be afraid to make a few revisions before starting to write real code.

 

The art is in getting the structure right. After that the writing of the source code is just filling in the pieces and a bit of debugging.

 

If your code needs to react to realtime incoming messages, you can not stop your program to look inside with a debugger.

What you can do is sprinkle simple messages through the code, and catch those with a LA. This is a pretty powerfull debugging technique, and I've written more about it if you follow the links in my signature.

 

Another approach is to look at code for data management between multiple busses, for example a translation between UART and I2C, or a hub / router for RF messages. The buffering code and latency issues are very similar, while UART, SPI, I2C or RF is just a small difference in the lowest layer of your data management stack.

Get the big picture clear first before focussing on those details.

 

 

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your requirements are not clear but possibly bit-banging both sides and modifying messages on bit level instead of whole bytes may work well for you. I2C is quite slow protocol, it should be doable.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

ScottMN wrote:

And clock stretching can do just that - hold up one side while you go talk to the other side

 

Clock stretching is part of I2C's protocol. It's purpose is for the slave to throttle the master when it needs more time to respond.  Sort of flow-control like.

 

In your case use this throttling mechanism to pause bus 1 while servicing bus 2.  Have the man-in-the-middle hold SCL low on bus 1, go talk to bus 2, then release bus SCL on bus 1 to continue the transaction.

 

https://www.i2c-bus.org/i2c-primer/clock-generation-stretching-arbitration/

 

Thank you for your reply, ScottMN. I know about clock stretching, and my existing solution that only handles Master Write operations already utilises that. Read operations are more difficult though, because they require me going back and forth between the 2 buses to pass read requests through, and acknowledging them only when I get acknowledged by the external master device.

 

 

Paulvdh wrote:

What you need are 2 interrupt driven I2C software stacks (or copies of the same (2 instances of a C++ class) and combine that with a bit of smart buffering.

2 Circular buffers are probably what you need, maybe the software is simpler if you have more circular buffers.

Thank you for your detailed reply, Paulvdh. It looks like I've already been using circular buffers without know they were called circular buffers. :) I don't really use interrupts on this project, because I found that they were interrupting my code at the wrong time, and never leaving enough time for me to send data through. However, I used the interrupt flags set by the hardware I2C interface, and manually checking them in a loop. It works well.

 

 

Paulvdh wrote:

And don't say too lightly you don't need a Logic Analyser. It can for example easily measure latency between receiving a message on a I2C bus and transmission on the other.

That latency between the I2C busses is probably one of the most important criterea to design around, and can be tricky, especially if the I2C busses work at different baudrates. Clock stretching can be usefull, but buffering multiple bytes before you start a transmission may be a better Idea.

 

I have a digital oscilloscope on my desk, so I already analysed the traffic between those devices. They use 400KHz baud rate. As I understand, I cannot rely on buffering multiple bytes in a read operation, as I need to get the data first, but I also need feedback after each byte whether the reading transaction is finished or not. Unfortunately, the length of these read operations vary between 1 and 64 bytes, so I don't know in advance how long a transaction will be.

 

 

Paulvdh wrote:

The next step is a bit of high level design. And this is still better done with pen and paper than with a PC.

Make a few graphical drawings of the data flows, and how you can separate and compartamentalise them.

Combine that with a bit of simple pseudo code, and check it with your graphical overview if it has a good chance of working.

Don't be afraid to make a few revisions before starting to write real code.

 

The art is in getting the structure right. After that the writing of the source code is just filling in the pieces and a bit of debugging.

 

If your code needs to react to realtime incoming messages, you can not stop your program to look inside with a debugger.

What you can do is sprinkle simple messages through the code, and catch those with a LA. This is a pretty powerfull debugging technique, and I've written more about it if you follow the links in my signature.

 

I think this will be the way to go. Thank you very much for your help.

 

 

Smajdalf wrote:
Your requirements are not clear but possibly bit-banging both sides and modifying messages on bit level instead of whole bytes may work well for you. I2C is quite slow protocol, it should be doable.

 

Hi Smajdalf, thank you for your reply. I don't really need to bit bang either sides, because I already have full control over both buses using the hardware I2C interfaces and their status flags. I apologise if my requirements were not too clear.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

< Rant about cheap LA's>

I still think an EUR 7 Logic analyser is in some ways superior to a modern  EUR 500 DSO.

In your case you want to monitor 2 I2C channels simultaneously, that's already 4 channels.

Then you want extra channels, to see if interrupts are triggered, or other interesting stuff happening in software (follow my signature).

 

The short logic analyser wires are also much easier to manage than bulky oscilloscope probes.

If I need some "serious" debugging with my LA, I tend to simple glue a 0.1" header with hot snot somewhere on the PCB.

On my bench I usually have a hot iron ready. (or after 20s).

Then solder enameled wires between the connector and interesting test points.

The LA itself is also so small that it can simply hang off a bunch of "Dupont wires", or use a short piece of flatcable.

This is a very easy, robust and convenient setup, and you can use your scope to probe around for more analog things.

 

An EUR 7 LA does not have particularly exciting hardware, but it is plenty fast enough to ***CONTINUOUSLY*** sample at a few MS/s and stream the data real time to a PC. Unfortunately Sigrok / Pulseview was still a bit buggy the last time I used it (oops, may be 1/2 year ago, I should do more projects...), but Pulseview should be able to capture a few minutes of continuous streaming if needed. (Maybe just juse an extra LA channel and uC pin to trigger the LA.

The software decoders in Sigrok / Pulseview are also likely to be much better than in your scope, and they can be easily expanded or write a new one for your specific data. Those decoders are typically 2 pages of python code, and Sigrok comes with about 100 usable examples.

 

For digital data I prever a big PC monitor and a mouse+ keyboard above the much smaller screen of an oscilloscope and it's buttons.

With a LA you do a lot of zooming and scrolling, and that works excellently with a mouse in Pulseview.

 

And then the price. Even for kids starting with digital electronics: If you have to save up 3 weeks of pocket money to be able to buy one it's still worth it. Ask one for your birthday.

</Rant about cheap LA's>

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Which logic analyzer are you referring to? Link please? :)

/Jakob Selbing

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

Something like this:

https://www.banggood.com/USB-Log...

 

Software: https://sigrok.org/wiki/Saleae_L...

 

The first time you need to use it, it will pay for itself. I have a few of these as they tend to die if you accidentally input more than 5V.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you ask this:

jaksel wrote:
Which logic analyzer are you referring to? Link please? :)

You did not follow this:

Paulvdh wrote:
(follow my signature).

Which leads to a demonstration of how to use such a LA for software debugging.

 

I'm also happy to be able to announce that just a few days ago the 100th protocol decoder has been added to Sigrok.

https://sigrok.org/blog/100th-supported-sigrok-protocol-decoder-cc1101

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Why did you "dismiss" Jim's (ki0bk) idea in #4 ?

 

Having thought about the problem for a bit and ignored the logic analyser posts, implementing a "shadow register set" from which I2C reads are; erm; read, is the way to go. I can immediately see in my head how this could work. The same cannot be said for any other technique although perhaps bypassing the I2C hardware and simply copying the GPIO levels from one bus to the other could work in read mode. However you have to detect the stop condition to leave this GPIO copying mode.

 

Concerning the "shadow register set" if there is a status register or similar you'll have to perform background reads on the camera bus to keep the shadow register up-to-date. Again this is very feasible.

 

On Logic Analysers: Agreed; a LA is definitely one of the most useful debugging tools around, but that usefulness doesn't apply here.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can't see any signatures for some reason...

/Jakob Selbing

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Copied it from my own signature:

https://www.avrfreaks.net/commen...

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:

Why did you "dismiss" Jim's (ki0bk) idea in #4 ?

 

Hi N.Winterbottom,

 

The reason I "dismissed" his idea is because I'm already using 2 buses, in kind of a similar setup. But rather than reading all registers in advance, I want to read them on demand, as the camera could internally change the values of those registers at any given point (it's only an assumption). So it makes sense to read / write the data real time. Both devices are happy with clock stretching, so time is not really an issue.

 

N.Winterbottom wrote:

Having thought about the problem for a bit and ignored the logic analyser posts, implementing a "shadow register set" from which I2C reads are; erm; read, is the way to go. I can immediately see in my head how this could work. The same cannot be said for any other technique although perhaps bypassing the I2C hardware and simply copying the GPIO levels from one bus to the other could work in read mode. However you have to detect the stop condition to leave this GPIO copying mode.

 

Concerning the "shadow register set" if there is a status register or similar you'll have to perform background reads on the camera bus to keep the shadow register up-to-date. Again this is very feasible.

 

This sounds quite interesting, but I don't think I have the knowledge to pull this off. I've only been doing C and embedded programming for 2 months.

 

I've been thinking about this a lot, I looked at other people's code, and all the logic analyzer talk made me check out Sigrok's implementation of the I2C protocol decoder, which used a state machine. This was kind of a new concept for me, as I didn't really need this in other programming languages I used (in mainly web projects). So instead of using multiple flags and their combination in trying to figure out what operation to do next, I can just use one state variable, which makes it much easier for me to see where I am in the process, and what needs to happen at any given time.