Date   

Securing BLE device communication without OOB pairing (multiple devices)

Vikrant More <vikrant8051@...>
 

Hello,
If I enabled encryption or authentication to access BLE device characteristic, we have to do OOB pairing.

But in some cases, it is not possible like budget LED lights. In this case, how to make secure communication at Zephyr App level using security keys ?

----------------------------------------------------------------------------------------------------
This is my implementation where there are 3 characteristics:
1) 1st (read) characteristic always generates 16 bytes of random data
2) 2nd (write) characteristic used for authentication
3) 3rd (write) characteristic which accepts commands

When BLE device is in factory reset mode, 
then Smartphone App read random data from 1st Characteristic & save it as #AdminKey (AES-128) for that device.

Then it again requests(read) another random data from 1st characteristic , encrypt it using #AdminKey & send to 2nd characteristic.

On BLE device side, it will decrypt data using #AdminKey & compare it with recently send random data. If data matched then BLE device saves #AdminKey on self flash memory.

So every device will have unique #AdminKey.

Now here after, Smartphone who send encrypted random data which is encrypted using #AdminKey to 2nd characteristic will get #admin access. (Random Data from 1st Characteristic)

Now if I wanna give access to my guests or family members, then in that case I have to set 16-bytes of #CommonKey (manually entered number) for all BLE devices.

Before this, Admin will Blink LED on BLE Device before transferring #CommonKey to it using Smartphone App. Once user confirm it, then only #CommonKey get transfer as command to 3rd characteristic & BLE device save it as #CommonKey.

As name suggest, #CommonKey is same for all devices. So here onward, Smartphone who send encrypted random data using #CommonKey will get #guest access of that BLE device. Using #guest access, in case of LED lights user can only do On/Off & intensity control.

So 3rd characteristic only accept commands when user authentic itself as #admin or #guest.

Can I go ahead with this method ?

But I think it is not secure, since data is exchanged over unencrypted link. Isn't it ?

Is somebody has better robust secure solution as per my requirements ?

Thank You !!














Re: bt_mesh_init error codes

Martin Woolley <mwoolley@...>
 

Johan, thanks a bunch for your comprehensive reply. You're correct that I started with the mesh_demo configuration and probably "borrowed" code from elsewhere as well, so that explains how I arrived here. The information on POSIX error numbers really helps... as I get more familiar with the code, hopefully I too will now be able to find where errors are being generated and deduce the reason.

I'll give this another try shortly!

-----Original Message-----
From: Johan Hedberg [mailto:johan.hedberg@intel.com]
Sent: 23 February 2018 19:00
To: Martin Woolley <mwoolley@bluetooth.com>
Cc: zephyr-users@lists.zephyrproject.org
Subject: Re: [Zephyr-users] bt_mesh_init error codes

Hi Martin,

On Fri, Feb 23, 2018, Martin Woolley wrote:
Hi, where can I find return codes for bt_mesh_init (and other
functions for that matter). I know a negative value means it failed
but I was hoping for some thing that provides a description of
specific return codes. I’m getting -35 from bt_mesh_init. It could be
because it’s Friday and been a long week but there may be a better
explanation 😊

bt_mesh_init failed with err -35
Negative 'int' type errors in Zephyr generally map to POSIX error numbers. E.g. 35 is the same as ENOTSUP. Looking through the possible code paths that bt_mesh_init() triggers, it seems likely that the following snippet in bt_pub_key_gen() (in hci_core.c) is at fault:

/*
* We check for both "LE Read Local P-256 Public Key" and
* "LE Generate DH Key" support here since both commands are needed for
* ECC support. If "LE Generate DH Key" is not supported then there
* is no point in reading local public key.
*/
if (!(bt_dev.supported_commands[34] & 0x02) ||
!(bt_dev.supported_commands[34] & 0x04)) {
BT_WARN("ECC HCI commands not available");
return -ENOTSUP;
}

Btw, you should enable CONFIG_BT_DEBUG_LOG=y since that would have given you the error message above (assuming this code path is at fault).
Adding the following to your configuration should make the issue go
away:

CONFIG_BT_TINYCRYPT_ECC=y

I suspect you arrived here because you started off with the mesh_demo configuration. The mesh_demo app does self-provisioning and as such doesn't need ECDH. Now you've apparently enabled the provisioning protocol, but not made sure that you have ECDH available. This needs to be explicitly enabled, since in split host & controller situations (where the controller is on a separate core) the controller may be providing the ECDH HCI commands, in which case ECDH support on the host side is not needed (beyond being able to send these HCI commands).

Johan


Re: bt_mesh_init error codes

Johan Hedberg
 

Hi Martin,

On Fri, Feb 23, 2018, Martin Woolley wrote:
Hi, where can I find return codes for bt_mesh_init (and other
functions for that matter). I know a negative value means it failed
but I was hoping for some thing that provides a description of
specific return codes. I’m getting -35 from bt_mesh_init. It could be
because it’s Friday and been a long week but there may be a better
explanation 😊

bt_mesh_init failed with err -35
Negative 'int' type errors in Zephyr generally map to POSIX error
numbers. E.g. 35 is the same as ENOTSUP. Looking through the possible
code paths that bt_mesh_init() triggers, it seems likely that the
following snippet in bt_pub_key_gen() (in hci_core.c) is at fault:

/*
* We check for both "LE Read Local P-256 Public Key" and
* "LE Generate DH Key" support here since both commands are needed for
* ECC support. If "LE Generate DH Key" is not supported then there
* is no point in reading local public key.
*/
if (!(bt_dev.supported_commands[34] & 0x02) ||
!(bt_dev.supported_commands[34] & 0x04)) {
BT_WARN("ECC HCI commands not available");
return -ENOTSUP;
}

Btw, you should enable CONFIG_BT_DEBUG_LOG=y since that would have given
you the error message above (assuming this code path is at fault).
Adding the following to your configuration should make the issue go
away:

CONFIG_BT_TINYCRYPT_ECC=y

I suspect you arrived here because you started off with the mesh_demo
configuration. The mesh_demo app does self-provisioning and as such
doesn't need ECDH. Now you've apparently enabled the provisioning
protocol, but not made sure that you have ECDH available. This needs to
be explicitly enabled, since in split host & controller situations
(where the controller is on a separate core) the controller may be
providing the ECDH HCI commands, in which case ECDH support on the host
side is not needed (beyond being able to send these HCI commands).

Johan


bt_mesh_init error codes

Martin Woolley <mwoolley@...>
 

Hi, where can I find return codes for bt_mesh_init (and other functions for that matter). I know a negative value means it failed but I was hoping for some thing that provides a description of specific return codes. I’m getting -35 from bt_mesh_init. It could be because it’s Friday and been a long week but there may be a better explanation 😊

 

bt_mesh_init failed with err -35

 

Thanks

 

Martin


Re: Data channels as ADV channel in #BluetoothMesh ? #bluetoothmesh

Johan Hedberg
 

Hi Vikrant,

On Thu, Feb 22, 2018, Vikrant More wrote:
In older version of Bluetooth, only 3 channels are used for advertising.
But in Bluetooth 5 we can use data channels for advertising.

Can we use data channels as ADV channel by configuring Zephyr
#BluetoothMesh stack ?

If this possible then, that could increase efficiency of Mesh performance.
Agreed, but unfortunately it's not possible with Bluetooth Mesh 1.0.
That said, it's possible we'll see a future Mesh specification version
that allows taking advantage of Bluetooth 5.0 features.

Johan


Data channels as ADV channel in #BluetoothMesh ? #bluetoothmesh

Vikrant More <vikrant8051@...>
 

Hi Johan, 
In older version of Bluetooth, only 3 channels are used for advertising. But in Bluetooth 5 we can use data channels for advertising.

Can we use data channels as ADV channel by configuring Zephyr #BluetoothMesh stack ?

If this possible then, that could increase efficiency of Mesh performance.

Thank You !!


Re: micro:bit GPIO read always returning 1

Martin Woolley <mwoolley@...>
 

As easy as that? 😊

 

OK, apparently I don’t understand what the documentation says for the flags:

 

GPIO_INT

GPIO pin to trigger interrupt.

GPIO_INT_EDGE

Do Edge trigger.

 

So, what I’m concluding is that you need to specify GPIO_INT to enable interrupts on the pin and then another flag like GPIO_INT_EDGE to say when in the signal processing you want the interrupt to occur? If so... makes sense (now). I thought saying GPIO_INT_EDGE was enough to enable interrupts *and* to say they should occur on edge. My bad.

 

Works anyway, thanks once again. Works even better with GPIO_INT_DEBOUNCE as well.

 

All the best

 

Martin

 

From: Vitor [mailto:vitor@...]
Sent: 22 February 2018 12:40
To: Martin Woolley <mwoolley@...>; zephyr-users@...
Subject: Re: [Zephyr-users] micro:bit GPIO read always returning 1

 

Hi Martin,

 

On Thu, Feb 22, 2018 at 6:06 AM, Martin Woolley <mwoolley@...> wrote:

thanks a lot for your guidance. Changing my pin config to

 

gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_PUD_PULL_DOWN );

 

without changing the circuit, now produces the behaviour I wanted.

 

Great!

 

If instead of polling, I want to use interrupts to trigger a call back when the pin goes either high or low, what should my pin config be? I’m getting no call back with the following:

 

  gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH );

  gpio_init_callback(&gpio_trigger_cb, triggered, BIT(PIN_0));

  gpio_add_callback(gpiob, &gpio_trigger_cb);

  gpio_pin_enable_callback(gpiob, PIN_0);

 

 

Just add this flag GPIO_INT on gpio_pin_configure():

gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH );

BR,

iha

 

 

 


Re: micro:bit GPIO read always returning 1

Vitor Massaru Iha
 

Hi Martin,


On Thu, Feb 22, 2018 at 6:06 AM, Martin Woolley <mwoolley@...> wrote:

thanks a lot for your guidance. Changing my pin config to

 

gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_PUD_PULL_DOWN );

 

without changing the circuit, now produces the behaviour I wanted.


Great!
 

If instead of polling, I want to use interrupts to trigger a call back when the pin goes either high or low, what should my pin config be? I’m getting no call back with the following:

 

  gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH );

  gpio_init_callback(&gpio_trigger_cb, triggered, BIT(PIN_0));

  gpio_add_callback(gpiob, &gpio_trigger_cb);

  gpio_pin_enable_callback(gpiob, PIN_0);

 


Just add this flag GPIO_INT on gpio_pin_configure():

gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH );

BR,
iha




Re: BLE reason codes

Carles Cufi
 

Hi Johannes,

 

Right, I see. No particular reason, feel free to send a pull request with the missing ones (at least the ones relevant to BLE).

 

Regards,

 

Carles

 

From: Johannes Hutter [mailto:johannes@...]
Sent: 22 February 2018 11:05
To: Cufi, Carles <Carles.Cufi@...>; zephyr-users@...
Subject: Re: [Zephyr-users] BLE reason codes

 

Hey Carles,

my question is whether there is a reason why not all of them are defined. Especially all the connection related errors (0x08: Connection Timeout, 0x0B Connection Already Exists etc.) are not defined but they are helpful if I want to change the application behavior based on the disconnection raeson.

Best regards,
Johannes

On 22.02.2018 10:35, Cufi, Carles wrote:

Hi Johannes,

 

Here they are:

 

https://github.com/zephyrproject-rtos/zephyr/blob/master/include/bluetooth/hci.h#L93

 

You can find the rest in the Bluetooth Specification, Vol 2. Part D.

 

Regards,

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Johannes Hutter
Sent: 22 February 2018 10:32
To: zephyr-users@...
Subject: [Zephyr-users] BLE reason codes

 

Hello Zephyr community,

 

if the connection between a BLE peripheral running Zephyr and a central device is lost, the "disconnected" callback is fired that passes the "reason code". I can't find any definitions of those disconnect reasons in the included bluetooth headers, but as I understand it they are part of the HCI error codes. Is there a reason not all of the possible error codes are defined in hci.h?

 

Thanks and best regards,

Johannes

--

Johannes Hutter | Embedded Software Engineer
Mail: johannes@...

Workaround GmbH (ProGlove)
Friedenstr. 4 | 81671 München

Managing Director: Thomas Kirchner
HRB: 216605 | AG München
USt.-IdNr.: DE298859320

 


Re: BLE reason codes

Johannes Hutter
 

Hey Carles,

my question is whether there is a reason why not all of them are defined. Especially all the connection related errors (0x08: Connection Timeout, 0x0B Connection Already Exists etc.) are not defined but they are helpful if I want to change the application behavior based on the disconnection raeson.

Best regards,
Johannes


On 22.02.2018 10:35, Cufi, Carles wrote:

Hi Johannes,

 

Here they are:

 

https://github.com/zephyrproject-rtos/zephyr/blob/master/include/bluetooth/hci.h#L93

 

You can find the rest in the Bluetooth Specification, Vol 2. Part D.

 

Regards,

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Johannes Hutter
Sent: 22 February 2018 10:32
To: zephyr-users@...
Subject: [Zephyr-users] BLE reason codes

 

Hello Zephyr community,

 

if the connection between a BLE peripheral running Zephyr and a central device is lost, the "disconnected" callback is fired that passes the "reason code". I can't find any definitions of those disconnect reasons in the included bluetooth headers, but as I understand it they are part of the HCI error codes. Is there a reason not all of the possible error codes are defined in hci.h?

 

Thanks and best regards,

Johannes

--

Johannes Hutter | Embedded Software Engineer
Mail: johannes@...

Workaround GmbH (ProGlove)
Friedenstr. 4 | 81671 München

Managing Director: Thomas Kirchner
HRB: 216605 | AG München
USt.-IdNr.: DE298859320


 


Re: BLE reason codes

Carles Cufi
 

Hi Johannes,

 

Here they are:

 

https://github.com/zephyrproject-rtos/zephyr/blob/master/include/bluetooth/hci.h#L93

 

You can find the rest in the Bluetooth Specification, Vol 2. Part D.

 

Regards,

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Johannes Hutter
Sent: 22 February 2018 10:32
To: zephyr-users@...
Subject: [Zephyr-users] BLE reason codes

 

Hello Zephyr community,

 

if the connection between a BLE peripheral running Zephyr and a central device is lost, the "disconnected" callback is fired that passes the "reason code". I can't find any definitions of those disconnect reasons in the included bluetooth headers, but as I understand it they are part of the HCI error codes. Is there a reason not all of the possible error codes are defined in hci.h?

 

Thanks and best regards,

Johannes

--

Johannes Hutter | Embedded Software Engineer
Mail: johannes@...

Workaround GmbH (ProGlove)
Friedenstr. 4 | 81671 München

Managing Director: Thomas Kirchner
HRB: 216605 | AG München
USt.-IdNr.: DE298859320

 


BLE reason codes

Johannes Hutter
 

Hello Zephyr community,


if the connection between a BLE peripheral running Zephyr and a central device is lost, the "disconnected" callback is fired that passes the "reason code". I can't find any definitions of those disconnect reasons in the included bluetooth headers, but as I understand it they are part of the HCI error codes. Is there a reason not all of the possible error codes are defined in hci.h?


Thanks and best regards,

Johannes

--

Johannes Hutter | Embedded Software Engineer
Mail: johannes@...


Workaround GmbH (ProGlove)
Friedenstr. 4 | 81671 München

Managing Director: Thomas Kirchner
HRB: 216605 | AG München
USt.-IdNr.: DE298859320


Re: Bluetooth mesh support of ble 5 features

Johan Hedberg
 

Hi Ashish,

On Thu, Feb 22, 2018, ashish.shukla@corvi.com wrote:
I want to know, does present version of mesh support features of bluetooth
5 such as long range?

If yes, how can I control transmitting power of radio, to limit or extend
range of signal ?
The 1.0 version of the Bluetooth Mesh Profile specification is built for
4.0 or newer Bluetooth versions, and as such any later features are not
part of it. That said, there are some benefits you get with HCI-based
stacks using Bluetooth 5.0 controllers, such as a lowered minimum
advertising interval (20ms in 5.0 compared to 100ms in earlier
versions).

Johan


Bluetooth mesh support of ble 5 features

ashish.shukla@corvi.com <ashish.shukla@...>
 

Hi Johan,

I want to know, does present version of mesh support features of bluetooth 5 such as long range?

If yes, how can I control transmitting power of radio, to limit or extend range of signal ?

--
Warm regards,
Ashish Shukla
Jr. Embedded Engineer
Research & Development


Please consider the environment before printing this e-mail or its attachments.

Disclaimer: The information contained herein (including any accompanying documents) is confidential and is intended solely for the addressee(s). If you have erroneously received this message, please immediately delete it and notify the sender. Also, if you are not the intended recipient, you are hereby notified that any disclosure, copying, distribution or taking any action in reliance on the contents of this message or any accompanying document is strictly prohibited and is unlawful. The organization is not responsible for any damage caused by a virus or alteration of the e-mail by a third party or otherwise. The contents of this message may not necessarily represent the views or policies of Corvi


Re: micro:bit GPIO read always returning 1

Vitor Massaru Iha
 

Hi Martin,

On Wed, Feb 21, 2018 at 10:08 AM, Martin Woolley <mwoolley@...> wrote:

Zephyr newbie question. I’m trying to poll an external pin on a micro:bit. I have a rocker switch with pull up resistor connected to it and the circuit works (tested with code written with microbit-dal APIs). My Zephyr GPIO reads always return 1, however. What am I doing wrong?



In order for the resistor to function as a pullup it must be placed before the switch, ie between PIN_0 and the VCC.
But note that you configured the GPIO port with the pullup flag, And the switch is connected between PIN_0 and VCC.
So the expected result is always 1.

Try to set the the GPIO port this way:

1)
gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN | GPIO_PUD_PULL_DOWN );
Without the resistor, It should work with the resistor, but that way it's unnecessary.

2) or
gpio_pin_configure(gpiob, PIN_0, GPIO_DIR_IN);
And connect the resistor between PIN_0 and GND;

There're other options, but I think you understand.

BR,
iha


Re: Unable to find the Kconfig program 'conf'

Martin Woolley <mwoolley@...>
 

Ah OK, thanks a lot. That explains it 😊 I was following the Getting Started Guide which I think still recommends just using the Windows cmd shell: http://docs.zephyrproject.org/getting_started/installation_win.html

 

I’m generally a Cygwin user so MSYS2 should be a pleasure.

 

From: Cufi, Carles [mailto:Carles.Cufi@...]
Sent: 21 February 2018 16:51
To: Martin Woolley <mwoolley@...>; zephyr-users@...
Subject: RE: Unable to find the Kconfig program 'conf'

 

Hi Martin,

 

Native Windows builds (i.e. without using MSYS2) were not supported in 1.10.

If you need to build 1.10 on Windows you need to use MSYS2, and the instructions are here:

 

http://docs.zephyrproject.org/getting_started/installation_win.html#using-msys2

 

This should cover how to build conf on MSYS2.

 

Regards,

 

Carles

 

From: Martin Woolley [mailto:mwoolley@...]
Sent: 21 February 2018 17:40
To: Cufi, Carles <Carles.Cufi@...>; zephyr-users@...
Subject: RE: Unable to find the Kconfig program 'conf'

 

I’m building on Windows. So where can I find “conf”? Google, somewhat uncharacteristically was no help with this 😊

 

From: Cufi, Carles [mailto:Carles.Cufi@...]
Sent: 21 February 2018 16:31
To: Martin Woolley <mwoolley@...>; zephyr-users@...
Subject: RE: Unable to find the Kconfig program 'conf'

 

Hi Martin,

 

Master no longer requires the “conf” executable, whereas 1.10 does. What OS are you building on?

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Martin Woolley
Sent: 21 February 2018 17:01
To: zephyr-users@...
Subject: [Zephyr-users] Unable to find the Kconfig program 'conf'

 

I just realised I've been working with the master branch i.e. version 1.10.99. I checked out tags/v1.10.0 and now nothing builds. Getting "Unable to find the Kconfig program 'conf'". Does anyone have any idea how to fix this? Thanks.


Re: Unable to find the Kconfig program 'conf'

Carles Cufi
 

Hi Martin,

 

Native Windows builds (i.e. without using MSYS2) were not supported in 1.10.

If you need to build 1.10 on Windows you need to use MSYS2, and the instructions are here:

 

http://docs.zephyrproject.org/getting_started/installation_win.html#using-msys2

 

This should cover how to build conf on MSYS2.

 

Regards,

 

Carles

 

From: Martin Woolley [mailto:mwoolley@...]
Sent: 21 February 2018 17:40
To: Cufi, Carles <Carles.Cufi@...>; zephyr-users@...
Subject: RE: Unable to find the Kconfig program 'conf'

 

I’m building on Windows. So where can I find “conf”? Google, somewhat uncharacteristically was no help with this 😊

 

From: Cufi, Carles [mailto:Carles.Cufi@...]
Sent: 21 February 2018 16:31
To: Martin Woolley <mwoolley@...>; zephyr-users@...
Subject: RE: Unable to find the Kconfig program 'conf'

 

Hi Martin,

 

Master no longer requires the “conf” executable, whereas 1.10 does. What OS are you building on?

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Martin Woolley
Sent: 21 February 2018 17:01
To: zephyr-users@...
Subject: [Zephyr-users] Unable to find the Kconfig program 'conf'

 

I just realised I've been working with the master branch i.e. version 1.10.99. I checked out tags/v1.10.0 and now nothing builds. Getting "Unable to find the Kconfig program 'conf'". Does anyone have any idea how to fix this? Thanks.


Re: Unable to find the Kconfig program 'conf'

Martin Woolley <mwoolley@...>
 

I’m building on Windows. So where can I find “conf”? Google, somewhat uncharacteristically was no help with this 😊

 

From: Cufi, Carles [mailto:Carles.Cufi@...]
Sent: 21 February 2018 16:31
To: Martin Woolley <mwoolley@...>; zephyr-users@...
Subject: RE: Unable to find the Kconfig program 'conf'

 

Hi Martin,

 

Master no longer requires the “conf” executable, whereas 1.10 does. What OS are you building on?

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Martin Woolley
Sent: 21 February 2018 17:01
To: zephyr-users@...
Subject: [Zephyr-users] Unable to find the Kconfig program 'conf'

 

I just realised I've been working with the master branch i.e. version 1.10.99. I checked out tags/v1.10.0 and now nothing builds. Getting "Unable to find the Kconfig program 'conf'". Does anyone have any idea how to fix this? Thanks.


Re: Unable to find the Kconfig program 'conf'

Carles Cufi
 

Hi Martin,

 

Master no longer requires the “conf” executable, whereas 1.10 does. What OS are you building on?

 

Carles

 

From: zephyr-users-bounces@... [mailto:zephyr-users-bounces@...] On Behalf Of Martin Woolley
Sent: 21 February 2018 17:01
To: zephyr-users@...
Subject: [Zephyr-users] Unable to find the Kconfig program 'conf'

 

I just realised I've been working with the master branch i.e. version 1.10.99. I checked out tags/v1.10.0 and now nothing builds. Getting "Unable to find the Kconfig program 'conf'". Does anyone have any idea how to fix this? Thanks.


Unable to find the Kconfig program 'conf'

Martin Woolley <mwoolley@...>
 

I just realised I've been working with the master branch i.e. version 1.10.99. I checked out tags/v1.10.0 and now nothing builds. Getting "Unable to find the Kconfig program 'conf'". Does anyone have any idea how to fix this? Thanks.

2121 - 2140 of 2703